Zsh-Startup-Dateien: .zprofile vs. .zshrc unter macOS und Linux
Automatische Übersetzung Dieser Artikel wurde automatisch aus der englischen Originalversion übersetzt.
Wenn sich Ihr Terminal langsam anfühlt oder eine Umgebungsvariable in einem Terminal vorhanden ist, aber in einem anderen nicht, liegt die Ursache oft im Startup-Modus von Zsh. Die beiden Dateien, zu denen die meisten greifen, sind ~/.zprofile und ~/.zshrc. Anhand des Dateinamens allein lässt sich jedoch nicht feststellen, ob eine Zeile dorthin gehört.
Zsh wählt Startup-Dateien anhand zweier unabhängiger Eigenschaften aus: ob die Shell eine Login-Shell und ob sie interaktiv ist. Terminal-Anwendungen unter macOS und Linux können unterschiedliche Kombinationen verwenden. Prüfen Sie daher die laufende Shell, statt anzunehmen, dass das Betriebssystem die Auswahl bestimmt.
TL;DR. Legen Sie die Initialisierung der Login-Session in
.zprofileund interaktives Verhalten in.zshrcab. Halten Sie.zshenvbei gesetztemRCSleise und minimal. Verschieben Sie keine interaktiven Hooks von Version-Managern nach.zprofileund sourcen Sie nicht das gesamte Profil aus.zshrc. Prüfen Sie stattdessen den vom Tool erzeugten Code und befolgen Sie die aktuellen Zsh-Anweisungen des Tools.
Zsh wertet zwei unabhängige Flags aus
Eine Shell kann sein:
- Login und interaktiv, wenn Apple Terminal so konfiguriert ist, dass es die Default login shell des Accounts öffnet
- Nicht-Login und interaktiv, etwa als verschachtelte
zshoder wenn viele Linux-Terminal-Konfigurationen sie starten - Login und nicht interaktiv, etwa als
zsh -lc 'command' - Nicht-Login und nicht interaktiv, etwa als
zsh -c 'command'
Terminal-Anwendungen, IDEs, Remote-Sessions, Multiplexer und explizite Shell-Flags können unterschiedliche Modi auswählen. Das Betriebssystem allein bestimmt das Ergebnis daher nicht.
Fragen Sie die laufende Shell, statt zu raten:
print -r -- "interactive=$options[interactive] login=$options[login]"
Die tatsächliche Startup-Reihenfolge
Zsh liest globale Dateien und anschließend die entsprechende Benutzerdatei. Bei den standardmäßigen RCS-Optionen ist die Reihenfolge der Benutzerdateien:
$ZDOTDIR/.zshenvbei jedem Zsh-Aufruf$ZDOTDIR/.zprofile, wenn die Shell eine Login-Shell ist$ZDOTDIR/.zshrc, wenn die Shell interaktiv ist$ZDOTDIR/.zlogin, wenn die Shell eine Login-Shell ist$ZDOTDIR/.zlogout, wenn eine Login-Shell normal beendet wird
Wenn ZDOTDIR nicht gesetzt ist, verwendet Zsh $HOME. Die systemweiten Dateipfade hängen von der Installation ab. Häufige Standardpfade sind /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin und /etc/zlogout (Zsh-Handbuch).
zsh -f startet mit nicht gesetztem RCS und überspringt daher normalerweise die oben genannten Benutzerdateien für $ZDOTDIR. /etc/zshenv wird immer gelesen und kann diese Option ändern.
.zprofile wird einmal pro Login-Shell-Prozess ausgeführt, nicht einmal pro Computer-Login. Wenn jeder neue Terminal-Tab eine Login-Shell startet, liest jeder Tab die Datei.
Was in welche Datei gehört
| Datei | Auswahlregel | Geeignete Inhalte | Vermeiden |
|---|---|---|---|
.zshenv | Jede Zsh mit gesetztem RCS | Seltene Variablen, die jeder Zsh-Prozess benötigt | Ausgabe, Aliases, Prompts, Netzwerkaufrufe, langsame Befehle |
.zprofile | Login-Shells | Pfad-Setup und exportierte Defaults der Login-Session | Interaktive Hooks, Completions, Key Bindings |
.zshrc | Interaktive Shells | Prompt, Completion, Aliases, Shell-Optionen, interaktive Tool-Hooks | Ausgabe oder Mutationen für Scripts |
.zlogin | Login-Shells nach .zshrc | Seltene nachgelagerte Aktionen der Login-Session | Konfiguration, die vor .zshrc erfolgen muss |
.zlogout | Beenden einer Login-Shell | Kleine Aufräum- oder Terminal-Reset-Aktionen | Wichtiger Zustand, der Crashes oder exec überdauern muss |
.zshenv bei gesetztem RCS schlicht halten
Wenn RCS gesetzt ist, liest jedes nicht-interaktive zsh -c .zshenv (Zsh-Handbuch). Ein echo, ein Aufruf eines Package-Managers oder ein teurer Subprozess an dieser Stelle kann die Command-Ausgabe verfälschen und Scripts verlangsamen. Die meisten Anwender benötigen in dieser Datei wenig oder gar nichts.
Eine in .zprofile exportierte Variable wird an die Kindprozesse dieser Login-Shell vererbt. Sie wird nicht automatisch in unabhängige GUI-Anwendungen, Services oder Shells injiziert, deren Parent die Datei nie gelesen hat.
.zprofile für Login-only-Setup verwenden
Ein kleines Profil könnte exportierte Defaults und einen idempotenten Pfad einrichten:
# ~/.zprofile
export EDITOR=nvim
export VISUAL=nvim
# Zsh ties the path array to the PATH scalar.
typeset -U path PATH
path=("$HOME/.local/bin" $path)
export PATH
typeset -U entfernt doppelte Array-Einträge. Dadurch wird wiederholtes Sourcen sicherer, dennoch sollten Startup-Dateien weiterhin unnötige Arbeit vermeiden.
.zshrc für interaktiven Zustand verwenden
# ~/.zshrc
setopt auto_cd hist_ignore_all_dups share_history
autoload -Uz compinit
compinit
alias ll='ls -lah'
bindkey -e
Aliases, Completion Widgets, Key Maps, Prompts und Directory-Change-Hooks gehören in die interaktive Shell, die sie verwendet.
Version-Manager überschreiten die einfache Dateigrenze
„Version-Manager gehören in .zprofile, weil sie langsam sind“ ist unzuverlässiger Rat. Ein Version-Manager kann mehrere Arten von Code erzeugen:
- einen statischen Pfad oder eine Root-Variable
- einen für Kindprozesse benötigten Command-Shim-Pfad
- Completion-Definitionen
- Shell-Funktionen, die die aktuelle Shell verändern
- Directory-Change-Hooks für automatisches Version-Switching
Die interaktiven Bestandteile müssen in jeder interaktiven Shell vorhanden sein, einschließlich einer verschachtelten zsh, die aus einem Editor gestartet wird. Das aktuelle fnm-Zsh-Setup legt diesen Hook daher in .zshrc ab:
eval "$(fnm env --use-on-cd --shell zsh)"
Das aktuelle pyenv-Zsh-Setup legt die vollständige interaktive Initialisierung ebenfalls in .zshrc ab:
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && path=("$PYENV_ROOT/bin" $path)
eval "$(pyenv init - zsh)"
Pyenv dokumentiert einen eingeschränkteren Modus pyenv init --path für das Setup des Shim-Pfads. Eine vollständige Initialisierung mit pyenv init installiert zusätzlich Completion und Shell-Funktionen. Prüfen Sie die aktuelle Dokumentation und die erzeugte Ausgabe eines Tools, bevor Sie sie aufteilen.
Wenn der Startup langsam ist, messen Sie ihn, bevor Sie Code in einen Modus verschieben, in dem er nicht mehr funktioniert.
Nicht das gesamte Profil aus .zshrc sourcen
Ein häufig verwendeter plattformübergreifender Workaround ist:
# Avoid this blanket coupling.
source ~/.zprofile
Dadurch werden Login-only-Aktionen zu Aktionen pro interaktiver Shell. Außerdem können Agent-Startup, Keychain-Zugriffe, Ausgabe oder Pfad-Mutationen wiederholt werden.
Wenn sowohl Login- als auch nicht als Login laufende interaktive Shells eine kleine Menge statischer Exporte benötigen, extrahieren Sie ein idempotentes Fragment und sourcen Sie es gezielt:
# ~/.config/zsh/environment.zsh
typeset -U path PATH
path=("$HOME/.local/bin" $path)
export PATH EDITOR=nvim VISUAL=nvim
# ~/.zprofile
source "$HOME/.config/zsh/environment.zsh"
# ~/.zshrc
if [[ ! -o login ]]; then
source "$HOME/.config/zsh/environment.zsh"
fi
# Interactive-only configuration follows.
Dieses Muster hält gemeinsame Daten von Login-seitigen Side Effects getrennt. Eine weitere gültige Möglichkeit besteht darin, Terminal oder Session Manager konsistent zu konfigurieren, statt beide Modi zu unterstützen.
Die langsame Shell oder fehlenden Zustand diagnostizieren
Reproduzieren Sie jeden Modus direkt:
zsh -lic 'print "login interactive"'
zsh -ic 'print "non-login interactive"'
zsh -lc 'print "login non-interactive"'
zsh -c 'print "non-login non-interactive"'
Messen Sie den Modus, den Benutzer tatsächlich starten:
time zsh -lic exit
time zsh -ic exit
Erstellen Sie einen Trace der Ausführung von Dateien und Zeilen, wenn die Quelle unklar ist:
PS4='%N:%i> ' zsh -xlic exit
Führen Sie den Trace in einem sauberen Test-Account aus oder prüfen Sie ihn vor der Weitergabe: Startup-Befehle können Tokens und andere sensible Werte expandieren.
Bei einer fehlenden ausführbaren Datei prüfen Sie sowohl den Modus als auch den Pfad:
print -r -- "interactive=$options[interactive] login=$options[login]"
print -l -- $path
whence -va python node uv
Fazit
Die entscheidende Unterscheidung lautet nicht „Umgebung versus Aliases“. Es geht um Login-only-Zustand gegenüber interaktivem Zustand sowie um die kleine Menge, die jeden Zsh-Prozess beeinflussen muss, wenn RCS gesetzt ist.
Bestätigen Sie die Shell-Flags, halten Sie Startup-Dateien leise und platzieren Sie jeden erzeugten Hook dort, wo alle benötigten Funktionen vorhanden sind. So bleibt die Konfiguration über verschiedene Terminal-Anwendungen hinweg portabel, ohne so zu tun, als hätten macOS und Linux jeweils genau einen festen Startmodus.