Bestanden bij de start van Zsh: .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 omgevingsvariabele in de ene terminal wel verschijnt en in de andere niet, ligt de oorzaak vaak bij de opstartmodus van Zsh. De twee bestanden waar de meeste mensen naar grijpen zijn ~/.zprofile en ~/.zshrc, maar de bestandsnaam alleen zegt niet of een regel daar thuishoort. Dit artikel laat zien welke opstartbestanden elk soort Zsh leest, wat in elk bestand thuishoort en met welke commando’s je vaststelt in wat voor shell je zit.

Zsh kiest opstartbestanden op basis van twee onafhankelijke eigenschappen: of de shell een login shell is en of hij interactief is. Terminalprogramma’s op macOS en Linux kunnen verschillende combinaties kiezen, dus onderzoek de draaiende shell in plaats van aan te nemen dat het besturingssysteem dit voor je bepaalt.

Een interactieve shell is bedoeld om commando’s in te voeren, meestal vanuit een terminal, en -i kan die modus afdwingen. Een login shell start in de loginmodus, gekozen door het startprogramma of -l; dat betekent niet dat de shell alleen start wanneer je je aanmeldt op de computer (Zsh-opties voor de shellmodus).

Zsh controleert twee onafhankelijke vlaggen

Een shell kan zijn:

  • login en interactief, zoals een nieuw macOS Terminal-venster of zsh -l
  • non-login en interactief, zoals een geneste zsh die je in een andere shell typt
  • login en niet-interactief, zoals zsh -lc 'command'
  • non-login en niet-interactief, zoals zsh -c 'command'

Het diagram zet de twee vlaggen op twee assen. Elke cel noemt de programma’s die dat soort shell meestal starten en markeert welke van de vijf gebruikersbestanden de shell leest.

Een twee-bij-twee raster van de login- en interactieve vlag. Elke cel noemt wie die shell start en markeert welke van .zshenv, .zprofile, .zshrc, .zlogin en .zlogout de shell leest; de non-login interactieve cel slaat .zprofile overEen twee-bij-twee raster van de login- en interactieve vlag. Elke cel noemt wie die shell start en markeert welke van .zshenv, .zprofile, .zshrc, .zlogin en .zlogout de shell leest; de non-login interactieve cel slaat .zprofile over

Het programma dat de shell start, kiest de modus. Dit zijn de standaardwaarden van gangbare launchers:

Gestart doorStandaardmodus
macOS Terminal, iTerm2login, interactief
GNOME Terminalnon-login, interactief
VS Code-terminal op macOSlogin, interactief (zsh -l)
VS Code-terminal op Linuxnon-login, interactief
ssh hostlogin, interactief
ssh host 'command'non-login, niet-interactief
nieuw tmux-venster, met een lege default-commandlogin, interactief
zsh getypt in een shellnon-login, interactief
zsh script.zshnon-login, niet-interactief

Jouw instellingen kunnen elke rij wijzigen. Om de werkelijke modus van de huidige shell te zien, voer je dit uit:

print -r -- "interactive=$options[interactive] login=$options[login]"

De werkelijke opstartvolgorde

Met de standaardopstartopties leest Zsh eerst de globale bestanden en daarna het bijbehorende gebruikersbestand. De volgorde van de gebruikersbestanden is:

  1. $ZDOTDIR/.zshenv voor elke Zsh-aanroep
  2. $ZDOTDIR/.zprofile als de shell een login shell is
  3. $ZDOTDIR/.zshrc als de shell interactief is
  4. $ZDOTDIR/.zlogin als de shell een login shell is
  5. $ZDOTDIR/.zlogout wanneer een interactieve login shell normaal afsluit

Als ZDOTDIR niet is ingesteld, gebruikt Zsh $HOME. De locaties van systeembrede bestanden hangen af van de installatie; de gangbare standaardlocaties zijn /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin en /etc/zlogout (Zsh-handleiding).

Op macOS voert /etc/zprofile path_helper uit, dat de systeemmappen vooraan in PATH zet. Stel je eigen PATH-items in in ~/.zprofile, dat daarna draait, niet in ~/.zshenv.

RCS is een Zsh-optie die standaard aanstaat en Zsh opstartbestanden na /etc/zshenv laat lezen. zsh -f schakelt die uit en slaat gewoonlijk de overige opstartbestanden over. /etc/zshenv draait nog steeds en kan RCS opnieuw inschakelen. Verdwijnt een probleem daar, onderzoek dan de overgeslagen opstartconfiguratie (Zsh-handleiding).

Het volgende diagram toont de volledige volgorde voor een nieuw macOS Terminal-venster, inclusief de globale bestanden, en wat er gebeurt met een PATH-item dat je toevoegt in ~/.zshenv in vergelijking met ~/.zprofile.

Opstartvolgorde van een macOS login shell van /etc/zshenv tot /etc/zlogout, met PATH-voorbeelden: ~/.local/bin toegevoegd in ~/.zshenv komt na path_helper als laatste, toegevoegd in ~/.zprofile blijft het eerstOpstartvolgorde van een macOS login shell van /etc/zshenv tot /etc/zlogout, met PATH-voorbeelden: ~/.local/bin toegevoegd in ~/.zshenv komt na path_helper als laatste, toegevoegd in ~/.zprofile blijft het eerst

.zprofile draait één keer per login shell-proces, niet één keer per aanmelding op de computer. Als elk nieuw terminaltabblad een login shell start, leest elk tabblad het.

Wat in welk bestand thuishoort

BestandSelectieregelGeschiktVermijd
.zshenvElke ZshZDOTDIR en zeldzame variabelen die elk Zsh-proces nodig heeftuitvoer, aliassen, prompts, netwerkaanroepen, trage commando’s
.zprofileLogin shellspadinstelling voor de loginsessie en geëxporteerde standaardwaardeninteractieve hooks, completions, toetsenbindingen
.zshrcInteractieve shellsprompt, completion, aliassen, shell-opties, interactieve tool-hooksuitvoer of wijzigingen die voor scripts bedoeld zijn
.zloginLogin shells, na .zshrczeldzame login-acties die na .zshrc moeten draaienconfiguratie die aan .zshrc vooraf moet gaan
.zlogoutAfsluiten van een login shellkleine opruim- of terminalreset-actiesbelangrijke toestand die crashes of exec moet overleven

Houd .zshenv kort en stil

Elke niet-interactieve zsh -c leest .zshenv (Zsh-handleiding). Een echo, een aanroep van een pakketbeheerder of een zwaar subproces daar kan de uitvoer van commando’s verstoren en scripts vertragen. De meeste gebruikers hebben weinig of niets nodig in dit bestand.

De gebruikelijke uitzondering is ZDOTDIR. Stel die in ~/.zshenv in om de andere opstartbestanden in een andere map te bewaren, zoals ~/.config/zsh.

Gebruik .zprofile voor instellingen die alleen bij login horen

Een geëxporteerde variabele in .zprofile wordt geërfd door de kindprocessen van die login shell. Ze wordt niet automatisch doorgegeven aan niet-gerelateerde GUI-applicaties, services of shells waarvan de ouder het bestand nooit las.

Een klein profiel op macOS kan Homebrew laden, geëxporteerde standaardwaarden instellen en PATH zonder dubbelingen opbouwen:

# ~/.zprofile
# The Homebrew installer recommends this line on Apple silicon.
eval "$(/opt/homebrew/bin/brew shellenv zsh)"

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 bewaart elk item één keer. Zonder deze optie voegt elke source van het bestand of elke geneste login shell dezelfde map opnieuw toe, en toont echo $PATH die meerdere keren.

Gebruik .zshrc voor interactieve toestand

Een nieuwe zsh-aanroep erft geëxporteerde variabelen. Hij erft geen aliassen, functies, shell-opties, toetsenbindingen of hooks van de bovenliggende shell. Elke interactieve shell moet ze opnieuw definiëren, en dat doet .zshrc. Zsh slaat .zshrc over bij normale niet-interactieve aanroepen.

Het diagram vergelijkt een geneste zsh en een zsh -c-scriptshell, beide gestart vanuit dezelfde login shell. In dit voorbeeld definieert alleen .zshrc de getoonde shelltoestand.

Een login shell en twee nieuwe zsh-aanroepen: beide krijgen PATH en EDITOR gekopieerd; de geneste zsh definieert de getoonde shelltoestand opnieuw vanuit .zshrc; de zsh -c-shell slaat die configuratie overEen login shell en twee nieuwe zsh-aanroepen: beide krijgen PATH en EDITOR gekopieerd; de geneste zsh definieert de getoonde shelltoestand opnieuw vanuit .zshrc; de zsh -c-shell slaat die configuratie over

Een minimale .zshrc stelt opties, completion, aliassen en toetsenbindingen in:

# ~/.zshrc
setopt auto_cd hist_ignore_all_dups share_history

autoload -Uz compinit
compinit

alias ll='ls -lah'
bindkey -e

Waar hooks van versiebeheerders horen

“Zet versiebeheerders in .zprofile omdat ze traag zijn” is onbetrouwbaar advies. Een versiebeheerder kan meerdere soorten code genereren:

  • een statisch pad of een root-variabele
  • een shim-pad voor commando’s dat kindprocessen nodig hebben
  • completion-definities
  • shellfuncties die de huidige shell wijzigen
  • hooks bij het wisselen van map voor automatisch wisselen van versie

De eerste twee worden geëxporteerd en gaan door naar kindshells. De rest is shelltoestand en moet dus bestaan in elke interactieve shell, ook in een geneste zsh die vanuit een editor start. De actuele Zsh-setup van fnm zet deze hook daarom in .zshrc:

eval "$(fnm env --use-on-cd --shell zsh)"

De actuele Zsh-setup van pyenv zet de volledige interactieve 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 modus, pyenv init --path, voor het instellen van het shim-pad, terwijl de volledige pyenv init ook completion en shellfuncties installeert. Bekijk de actuele documentatie en gegenereerde uitvoer van een tool voordat je die opsplitst.

Is het opstarten traag, meet dan eerst voordat je code verplaatst naar een modus waarin die niet meer werkt. In de diagnosesectie staat hoe.

Source het hele profiel niet vanuit .zshrc

Een veelgebruikte omweg die op elk platform werkt is:

# Avoid this blanket coupling.
source ~/.zprofile

Dat maakt van acties die alleen bij login horen acties die in elke interactieve shell draaien. Het kan ssh-agent opnieuw starten, opnieuw om toegang tot de keychain vragen, uitvoer printen of PATH-items dubbel toevoegen.

Hebben zowel login- als non-login interactieve shells een kleine set statische exports nodig, haal dan een idempotent fragment apart 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.

Een login shell leest environment.zsh via .zprofile. Een non-login interactieve shell leest het via .zshrc. Login-acties zoals ssh-agent blijven in .zprofile, dus dit patroon houdt gedeelde gegevens gescheiden van bijwerkingen bij login. Een andere optie is je terminal een login shell te laten starten, zodat elk tabblad .zprofile leest.

Diagnoseer de shell die traag is of toestand mist

Begin bij het symptoom: een trage shell of een ontbrekende instelling.

Reproduceer elke modus rechtstreeks:

zsh -lic 'print "login interactive"'
zsh -ic  'print "non-login interactive"'
zsh -lc  'print "login non-interactive"'
zsh -c   'print "non-login non-interactive"'

Gebruik bij een trage shell time voor de totale duur, zprof om te zien welke functie de tijd kost en de -x-trace om te zien welk bestand en welke regel draait.

Meet de modus die je terminal start:

time zsh -lic exit
time zsh -ic exit

Laad de profiler bovenaan .zshrc en print het rapport onderaan. De volgende nieuwe shell print een tabel met functies gesorteerd op tijd:

# first line of ~/.zshrc
zmodload zsh/zprof
# last line of ~/.zshrc
zprof

Trace de uitvoering per bestand en regel als de bron onduidelijk is:

PS4='%N:%i> ' zsh -xlic exit

Draai de trace in een schoon testaccount of bekijk hem voordat je hem deelt: opstartcommando’s kunnen tokens en andere gevoelige waarden uitschrijven.

Onderzoek bij een ontbrekend uitvoerbaar bestand zowel de modus als het pad:

print -r -- "interactive=$options[interactive] login=$options[login]"
print -l -- $path
whence -va python node uv

Om een wijziging toe te passen open je een nieuw tabblad of voer je exec zsh -l (login) of exec zsh (non-login) uit. source ~/.zshrc voert het bestand opnieuw uit in de huidige shell en behoudt wat de oude versie had ingesteld.

Conclusie

Het nuttige onderscheid is niet “omgeving versus aliassen”. Het is toestand die alleen bij login hoort versus interactieve toestand, plus het kleine deel dat elk Zsh-proces moet beïnvloeden.

Gedraagt een shell zich verkeerd, voer dan het print-commando uit de eerste sectie uit en controleer of de instelling in een bestand staat dat die modus leest.

Referenties