Pliki startowe Zsh: .zprofile a .zshrc w systemach macOS i Linux

Tłumaczenie automatyczne

Ten artykuł został automatycznie przetłumaczony z angielskiego oryginału.

Jeśli terminal działa wolno albo zmienna środowiskowa jest widoczna w jednym terminalu, a w innym nie, przyczyną jest często tryb startowy Zsh. Najczęściej sięgamy po ~/.zprofile i ~/.zshrc, ale sama nazwa pliku nie mówi, czy dana linia tam pasuje. Ten artykuł pokazuje, które pliki startowe czyta każdy rodzaj Zsh, co należy do każdego z nich i jakie polecenia mówią, w jakiej powłoce jesteś.

Zsh wybiera pliki startowe na podstawie dwóch niezależnych właściwości: czy powłoka jest powłoką logowania (ang. login shell) i czy jest interaktywna. Aplikacje terminalowe w systemach macOS i Linux mogą wybierać różne kombinacje, dlatego sprawdź uruchomioną powłokę zamiast zakładać, że decyduje o tym system operacyjny.

Powłoka interaktywna służy do wprowadzania poleceń, zwykle z terminala, a -i może wymusić ten tryb. Powłoka logowania uruchamia się w trybie logowania wybranym przez program uruchamiający lub -l; nie oznacza to, że uruchamia się tylko podczas logowania do komputera (opcje stanu powłoki Zsh).

Zsh sprawdza dwie niezależne flagi

Powłoka może być:

  • logowania i interaktywna, jak nowe okno Terminala w macOS lub zsh -l
  • nielogowania i interaktywna, jak zagnieżdżony zsh wpisany w innej powłoce
  • logowania i nieinteraktywna, jak zsh -lc 'command'
  • nielogowania i nieinteraktywna, jak zsh -c 'command'

Diagram stawia obie flagi na dwóch osiach. Każda komórka wymienia programy, które zwykle uruchamiają taką powłokę, i zaznacza, które z pięciu plików użytkownika czyta.

Siatka dwa na dwa z flagami logowania i trybu interaktywnego. Każda komórka podaje, kto uruchamia taką powłokę, i zaznacza, które z .zshenv, .zprofile, .zshrc, .zlogin i .zlogout czyta; interaktywna powłoka bez logowania pomija .zprofileSiatka dwa na dwa z flagami logowania i trybu interaktywnego. Każda komórka podaje, kto uruchamia taką powłokę, i zaznacza, które z .zshenv, .zprofile, .zshrc, .zlogin i .zlogout czyta; interaktywna powłoka bez logowania pomija .zprofile

Tryb wybiera program, który uruchamia powłokę. Oto domyślne ustawienia popularnych programów:

Uruchamiane przezTryb domyślny
macOS Terminal, iTerm2logowania, interaktywny
GNOME Terminalnielogowania, interaktywny
terminal VS Code w macOSlogowania, interaktywny (zsh -l)
terminal VS Code w Linuksienielogowania, interaktywny
ssh hostlogowania, interaktywny
ssh host 'command'nielogowania, nieinteraktywny
nowe okno tmux z pustym default-commandlogowania, interaktywny
zsh wpisany w powłocenielogowania, interaktywny
zsh script.zshnielogowania, nieinteraktywny

Twoje ustawienia mogą zmienić każdy wiersz. Aby zobaczyć rzeczywisty tryb bieżącej powłoki, uruchom:

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

Rzeczywista kolejność startu

Przy domyślnych opcjach startowych Zsh czyta pliki globalne, a potem odpowiadający im plik użytkownika. Kolejność plików użytkownika jest taka:

  1. $ZDOTDIR/.zshenv przy każdym uruchomieniu Zsh
  2. $ZDOTDIR/.zprofile, jeśli powłoka jest powłoką logowania
  3. $ZDOTDIR/.zshrc, jeśli powłoka jest interaktywna
  4. $ZDOTDIR/.zlogin, jeśli powłoka jest powłoką logowania
  5. $ZDOTDIR/.zlogout, gdy interaktywna powłoka logowania kończy się normalnie

Jeśli ZDOTDIR nie jest ustawiony, Zsh używa $HOME. Położenie plików systemowych zależy od instalacji; typowe domyślne to /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin i /etc/zlogout (podręcznik Zsh).

W macOS /etc/zprofile uruchamia path_helper, który umieszcza katalogi systemowe na początku PATH. Własne wpisy PATH ustawiaj w ~/.zprofile, który działa później, a nie w ~/.zshenv.

RCS to domyślnie włączona opcja Zsh, która pozwala czytać pliki startowe po /etc/zshenv. zsh -f ją wyłącza i zwykle pomija pozostałe pliki startowe. /etc/zshenv nadal się wykonuje i może ponownie włączyć RCS. Jeśli problem tam znika, sprawdź pominiętą konfigurację startową (podręcznik Zsh).

Następny diagram pokazuje pełną kolejność dla nowego okna Terminala w macOS, razem z plikami globalnymi, oraz to, co dzieje się z wpisem PATH dodanym w ~/.zshenv w porównaniu z ~/.zprofile.

Kolejność startu powłoki logowania w macOS od /etc/zshenv do /etc/zlogout, z przykładami PATH: ~/.local/bin dodany w ~/.zshenv ląduje na końcu po path_helper, dodany w ~/.zprofile zostaje na początkuKolejność startu powłoki logowania w macOS od /etc/zshenv do /etc/zlogout, z przykładami PATH: ~/.local/bin dodany w ~/.zshenv ląduje na końcu po path_helper, dodany w ~/.zprofile zostaje na początku

.zprofile działa raz na proces powłoki logowania, a nie raz na logowanie do komputera. Jeśli każda nowa karta terminala uruchamia powłokę logowania, każda karta czyta ten plik.

Co należy do którego pliku

PlikReguła wyboruCo pasujeUnikaj
.zshenvKażdy ZshZDOTDIR i rzadkie zmienne potrzebne każdemu procesowi Zshwyjścia, aliasów, ustawień prompt, wywołań sieciowych, wolnych poleceń
.zprofilePowłoki logowaniaustawienie ścieżek sesji logowania i eksportowane wartości domyślneinteraktywnych hooków, uzupełniania, przypisań klawiszy
.zshrcPowłoki interaktywneprompt, uzupełnianie, aliasy, opcje powłoki, interaktywne hooki narzędziwyjścia lub zmian przeznaczonych dla skryptów
.zloginPowłoki logowania, po .zshrcrzadkie akcje logowania, które muszą działać po .zshrckonfiguracji, która musi poprzedzać .zshrc
.zlogoutZakończenie powłoki logowaniadrobne czyszczenie lub reset terminalaważnego stanu, który musi przetrwać awarię lub exec

Nie rozbudowuj .zshenv i nie wypisuj w nim danych

Każde nieinteraktywne zsh -c czyta .zshenv (podręcznik Zsh). echo, wywołanie menedżera pakietów albo kosztowny podproces w tym pliku mogą zepsuć wyjście poleceń i spowolnić skrypty. Większość użytkowników potrzebuje w nim niewiele albo nic.

Typowym wyjątkiem jest ZDOTDIR. Ustaw go w ~/.zshenv, aby trzymać pozostałe pliki startowe w innym katalogu, na przykład ~/.config/zsh.

Używaj .zprofile do konfiguracji tylko dla logowania

Zmienną wyeksportowaną w .zprofile dziedziczą procesy potomne tej powłoki logowania. Nie jest ona automatycznie wstrzykiwana do niezwiązanych aplikacji graficznych, usług ani powłok, których proces nadrzędny nigdy nie czytał tego pliku.

Mały profil w macOS może wczytać Homebrew, ustawić eksportowane wartości domyślne i zbudować PATH bez duplikatów:

# ~/.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 zachowuje każdy wpis tylko raz. Bez niego każde source tego pliku lub zagnieżdżona powłoka logowania dodaje ten sam katalog ponownie, a echo $PATH pokazuje go kilka razy.

Używaj .zshrc do stanu interaktywnego

Nowo uruchomiony zsh dziedziczy wyeksportowane zmienne. Nie dziedziczy aliasów, funkcji, opcji powłoki, przypisań klawiszy ani hooków powłoki nadrzędnej. Każda powłoka interaktywna musi zdefiniować je od nowa, i to robi .zshrc. Zsh pomija .zshrc przy zwykłych uruchomieniach nieinteraktywnych.

Diagram porównuje zagnieżdżony zsh i powłokę skryptu zsh -c uruchomione z tej samej powłoki logowania. W tym przykładzie tylko .zshrc definiuje pokazany stan powłoki.

Powłoka logowania i dwa nowo uruchomione procesy zsh: oba dostają kopie PATH i EDITOR; zagnieżdżony zsh ponownie definiuje pokazany stan z .zshrc; powłoka zsh -c pomija tę konfiguracjęPowłoka logowania i dwa nowo uruchomione procesy zsh: oba dostają kopie PATH i EDITOR; zagnieżdżony zsh ponownie definiuje pokazany stan z .zshrc; powłoka zsh -c pomija tę konfigurację

Minimalny .zshrc ustawia opcje, uzupełnianie, aliasy i przypisania klawiszy:

# ~/.zshrc
setopt auto_cd hist_ignore_all_dups share_history

autoload -Uz compinit
compinit

alias ll='ls -lah'
bindkey -e

Gdzie trafiają hooki menedżerów wersji

Rada „umieść menedżery wersji w .zprofile, bo są wolne” jest zawodna. Menedżer wersji może generować kilka rodzajów kodu:

  • statyczną ścieżkę lub zmienną katalogu głównego
  • ścieżkę shimów poleceń potrzebną procesom potomnym
  • definicje uzupełniania
  • funkcje powłoki, które zmieniają bieżącą powłokę
  • hooki zmiany katalogu do automatycznego przełączania wersji

Dwa pierwsze są eksportowane i trafiają do powłok potomnych. Reszta to stan powłoki, więc musi istnieć w każdej powłoce interaktywnej, także w zagnieżdżonym zsh uruchomionym z edytora. Dlatego obecna konfiguracja fnm dla Zsh umieszcza ten hook w .zshrc:

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

Obecna konfiguracja pyenv dla Zsh również umieszcza pełną inicjalizację interaktywną w .zshrc:

export PYENV_ROOT="$HOME/.pyenv"
[[ -d $PYENV_ROOT/bin ]] && path=("$PYENV_ROOT/bin" $path)
eval "$(pyenv init - zsh)"

Dokumentacja pyenv opisuje węższy tryb pyenv init --path do ustawiania ścieżki shimów, a pełne pyenv init instaluje też uzupełnianie i funkcje powłoki. Zanim rozdzielisz konfigurację narzędzia, sprawdź jego aktualną dokumentację i wygenerowany kod.

Jeśli start jest wolny, zmierz go, zanim przeniesiesz kod do trybu, w którym przestaje działać. Sekcja o diagnozie pokazuje, jak.

Nie wczytuj całego profilu z .zshrc

Częste obejście między platformami wygląda tak:

# Avoid this blanket coupling.
source ~/.zprofile

To zamienia akcje tylko dla logowania w akcje przy każdej powłoce interaktywnej. Może ponownie uruchomić ssh-agent, ponownie poprosić o dostęp do pęku kluczy, wypisać dane albo dodać wpisy PATH dwa razy.

Jeśli zarówno powłoki logowania, jak i interaktywne powłoki nielogowania potrzebują małego zestawu statycznych eksportów, wydziel idempotentny fragment i wczytaj go świadomie:

# ~/.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.

Powłoka logowania czyta environment.zsh przez .zprofile. Interaktywna powłoka nielogowania czyta go przez .zshrc. Działania logowania, takie jak ssh-agent, zostają w .zprofile, więc ten wzorzec oddziela wspólne dane od skutków ubocznych logowania. Inna możliwość to ustawić terminal tak, by uruchamiał powłokę logowania; wtedy każda karta czyta .zprofile.

Diagnozuj powłokę, która jest wolna lub nie ma stanu

Zacznij od objawu: wolna powłoka albo brakujące ustawienie.

Odtwórz każdy tryb bezpośrednio:

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

Przy wolnej powłoce użyj time do czasu całkowitego, zprof, by zobaczyć, która funkcja zajmuje czas, i śladu -x, by zobaczyć, który plik i która linia się wykonują.

Zmierz tryb, który uruchamia twój terminal:

time zsh -lic exit
time zsh -ic exit

Wczytaj profiler na początku .zshrc i wypisz jego raport na końcu. Następna nowa powłoka wypisze tabelę funkcji posortowaną według czasu:

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

Gdy źródło jest niejasne, prześledź wykonanie plików i linii:

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

Uruchom ślad na czystym koncie testowym albo przejrzyj go przed udostępnieniem: polecenia startowe mogą rozwijać wartości typu token i inne wrażliwe wartości.

Przy brakującym pliku wykonywalnym sprawdź tryb i ścieżkę:

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

Aby zastosować zmianę, otwórz nową kartę albo uruchom exec zsh -l (logowania) lub exec zsh (nielogowania). source ~/.zshrc uruchamia plik ponownie w bieżącej powłoce i zachowuje wszystko, co ustawiła stara wersja.

Podsumowanie

Przydatne rozróżnienie to nie „środowisko kontra aliasy”. Chodzi o stan tylko dla logowania kontra stan interaktywny oraz niewielką część, która musi działać w każdym procesie Zsh.

Gdy powłoka zachowuje się źle, uruchom polecenie print z pierwszej sekcji, a potem sprawdź, czy ustawienie leży w pliku, który ten tryb czyta.

Źródła