Zsh Startup Files: .zprofile versus .zshrc op macOS en Linux
Automatische vertaling Dit artikel is automatisch vertaald vanuit de oorspronkelijke Engelse versie.
Als je terminal traag aanvoelt of een environment variable wel in de ene terminal maar niet in de andere verschijnt, ligt de oorzaak vaak bij de startup mode van Zsh. De twee bestanden waar de meeste mensen naar grijpen zijn ~/.zprofile en ~/.zshrc, maar geen van beide bestandsnamen zegt op zichzelf waar een regel thuishoort.
Zsh kiest startup files op basis van twee onafhankelijke eigenschappen: of de shell een login shell is en of deze interactive is. Terminalapplicaties op macOS en Linux kunnen verschillende combinaties kiezen. Inspecteer daarom de actieve shell in plaats van ervan uit te gaan dat het besturingssysteem dit voor je bepaalt.
TL;DR. Plaats initialisatie voor de login session in
.zprofileen interactive gedrag in.zshrc. Houd.zshenvstil en minimaal wanneerRCSis ingesteld. Verplaats interactive version-manager hooks niet naar.zprofileen source niet het volledige profile vanuit.zshrc; inspecteer de door de tool gegenereerde code en volg de actuele Zsh-instructies.
Zsh evalueert twee onafhankelijke flags
Een shell kan zijn:
- login en interactive, wanneer Apple Terminal is ingesteld om de Default login shell van het account te openen
- non-login en interactive, zoals bij een geneste
zshof bij veel Linux-terminalconfiguraties - login en non-interactive, zoals bij
zsh -lc 'command' - non-login en non-interactive, zoals bij
zsh -c 'command'
Terminalapplicaties, IDE’s, remote sessions, multiplexers en expliciete shell flags kunnen verschillende modes kiezen. Het besturingssysteem alleen bepaalt het resultaat dus niet.
Vraag het aan de actieve shell in plaats van te gokken:
print -r -- "interactive=$options[interactive] login=$options[login]"
De daadwerkelijke startup order
Zsh leest eerst globale files en daarna het bijbehorende user file. Met de standaard RCS options is de volgorde van de user files:
$ZDOTDIR/.zshenvbij elke Zsh-invocation$ZDOTDIR/.zprofileals de shell een login shell is$ZDOTDIR/.zshrcals de shell interactive is$ZDOTDIR/.zloginals de shell een login shell is$ZDOTDIR/.zlogoutwanneer een login shell normaal afsluit
Als ZDOTDIR niet is ingesteld, gebruikt Zsh $HOME. De locaties van system-wide files zijn installatieafhankelijk; de gebruikelijke defaults zijn /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin en /etc/zlogout (Zsh manual).
zsh -f start met RCS unset en slaat daarom normaal gesproken de bovenstaande $ZDOTDIR user files over; /etc/zshenv wordt altijd gelezen en kan die option wijzigen.
.zprofile wordt eenmaal per login-shell process uitgevoerd, niet eenmaal per login op de computer. Als elk nieuw terminaltabblad een login shell start, leest elk tabblad het bestand.
Wat hoort in elk bestand?
| File | Selectieregel | Goede kandidaten | Vermijden |
|---|---|---|---|
.zshenv | Elke Zsh met RCS ingesteld | Zeldzame variables die elk Zsh-process nodig heeft | output, aliases, prompts, network calls, trage commands |
.zprofile | Login shells | path setup voor de login session en geëxporteerde defaults | interactive hooks, completions, key bindings |
.zshrc | Interactive shells | prompt, completion, aliases, shell options, interactive tool hooks | output of mutations die voor scripts bedoeld zijn |
.zlogin | Login shells, na .zshrc | zeldzame post-interactive login actions | configuration die vóór .zshrc moet worden uitgevoerd |
.zlogout | Afsluiten van een login shell | kleine cleanup- of terminal-resetacties | belangrijke state die crashes of exec moet overleven |
Houd .zshenv saai wanneer RCS is ingesteld
Wanneer RCS is ingesteld, leest elke non-interactive zsh -c .zshenv (Zsh manual). Een echo, package-manager call of dure subprocess daar kan command output beschadigen en scripts vertragen. De meeste gebruikers hebben weinig tot niets in dit bestand nodig.
Een geëxporteerde variable in .zprofile wordt geërfd door children van die login shell. De variable wordt niet automatisch geïnjecteerd in niet-gerelateerde GUI-applicaties, services of shells waarvan de parent het bestand nooit heeft gelezen.
Gebruik .zprofile voor login-only setup
Een klein profile kan geëxporteerde defaults en een idempotent path instellen:
# ~/.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 verwijdert dubbele array entries. Dat maakt herhaald sourcen veiliger, al moeten startup files nog steeds onnodig werk vermijden.
Gebruik .zshrc voor interactive state
# ~/.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 en directory-change hooks horen bij de interactive shell die ze gebruikt.
Version managers overschrijden de eenvoudige bestandsgrens
“Plaats version managers in .zprofile omdat ze traag zijn” is onbetrouwbaar advies. Een version manager kan verschillende soorten code genereren:
- een statisch path of root variable
- een command-shim path dat child processes nodig hebben
- completion definitions
- shell functions die de huidige shell wijzigen
- directory-change hooks voor automatisch wisselen van versions
De interactive onderdelen moeten in elke interactive shell aanwezig zijn, inclusief een geneste zsh die vanuit een editor wordt gestart. De actuele fnm Zsh setup plaatst deze hook daarom in .zshrc:
eval "$(fnm env --use-on-cd --shell zsh)"
De actuele pyenv Zsh setup plaatst volledige interactive initialisatie eveneens in .zshrc:
export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && path=("$PYENV_ROOT/bin" $path)
eval "$(pyenv init - zsh)"
Pyenv documenteert een beperktere pyenv init --path-mode voor shim-path setup, terwijl volledige pyenv init ook completion en shell functions installeert. Inspecteer de actuele documentatie en gegenereerde output van een tool voordat je deze opsplitst.
Als startup traag is, meet dit dan eerst voordat je code verplaatst naar een mode waarin deze niet meer werkt.
Source niet het volledige profile vanuit .zshrc
Een veelgebruikte cross-platform workaround is:
# Avoid this blanket coupling.
source ~/.zprofile
Daarmee worden login-only actions per interactive shell uitgevoerd. Ook kunnen agent startup, keychain access, output of path mutation worden herhaald.
Als zowel login als non-login interactive shells een kleine set statische exports nodig hebben, haal dan een idempotent fragment uit de configuratie en source dat bewust:
# ~/.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.
Dit pattern houdt gedeelde data gescheiden van login side effects. Een andere geldige keuze is om de terminal- of session manager consistent te configureren in plaats van beide modes te ondersteunen.
Diagnoseer de shell die traag is of state mist
Reproduceer elke mode direct:
zsh -lic 'print "login interactive"'
zsh -ic 'print "non-login interactive"'
zsh -lc 'print "login non-interactive"'
zsh -c 'print "non-login non-interactive"'
Meet de mode die gebruikers daadwerkelijk starten:
time zsh -lic exit
time zsh -ic exit
Trace file- en line-execution wanneer niet duidelijk is welke source wordt uitgevoerd:
PS4='%N:%i> ' zsh -xlic exit
Voer de trace uit in een schoon testaccount of inspecteer deze voordat je hem deelt: startup commands kunnen tokens en andere gevoelige waarden expanderen.
Bij een ontbrekende executable inspecteer je zowel de mode als het path:
print -r -- "interactive=$options[interactive] login=$options[login]"
print -l -- $path
whence -va python node uv
Conclusie
Het relevante onderscheid is niet “environment versus aliases”. Het gaat om login-only state versus interactive state, plus de kleine hoeveelheid die elk Zsh-process moet beïnvloeden wanneer RCS is ingesteld.
Controleer de shell flags, houd startup files stil en plaats elke gegenereerde hook waar alle vereiste features beschikbaar zijn. Zo blijft de configuratie portable tussen terminalapplicaties, zonder te doen alsof macOS en Linux elk één vaste launch mode hebben.