Ficheiros de arranque do Zsh: .zprofile vs .zshrc no macOS e Linux

Tradução automática Este artigo foi traduzido automaticamente a partir da versão original em inglês.

Se o terminal parece lento, ou se uma variável de ambiente aparece num terminal mas não noutro, a causa é muitas vezes o modo de arranque do Zsh. Os dois ficheiros a que a maioria das pessoas recorre são ~/.zprofile e ~/.zshrc, mas nenhum dos nomes, por si só, indica se uma determinada linha deve ficar aí.

O Zsh escolhe os ficheiros de arranque com base em duas propriedades independentes: se a shell é uma login shell e se é interativa. As aplicações de terminal no macOS e no Linux podem escolher combinações diferentes, por isso deve inspeccionar a shell em execução em vez de assumir que o sistema operativo decide por si.

Em resumo. Coloque a inicialização da sessão de login em .zprofile e o comportamento interactivo em .zshrc. Mantenha .zshenv silencioso e mínimo quando RCS estiver definido. Não mova hooks interactivos de gestores de versões para .zprofile nem faça source do perfil inteiro a partir de .zshrc; inspeccione o código gerado pela ferramenta e siga as instruções actuais para Zsh.

O Zsh avalia duas flags independentes

Ficheiros de arranque do Zsh seleccionados pelos modos de login e interactivoFicheiros de arranque do Zsh seleccionados pelos modos de login e interactivo

Uma shell pode ser:

  • de login e interactiva, quando o Apple Terminal está configurado para abrir a shell de login predefinida da conta
  • não-login e interactiva, como uma zsh aninhada ou em muitas configurações de terminais Linux
  • de login e não interactiva, como zsh -lc 'command'
  • não-login e não interactiva, como zsh -c 'command'

As aplicações de terminal, IDEs, sessões remotas, multiplexers e flags explícitas da shell podem escolher modos diferentes; por isso, o sistema operativo, por si só, não determina o resultado.

Pergunte à shell em execução em vez de adivinhar:

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

A ordem de arranque efectiva

O Zsh lê os ficheiros globais e, em seguida, o ficheiro de utilizador correspondente. Com as opções RCS predefinidas, a ordem dos ficheiros de utilizador é:

  1. $ZDOTDIR/.zshenv em todas as invocações do Zsh
  2. $ZDOTDIR/.zprofile se a shell for uma login shell
  3. $ZDOTDIR/.zshrc se a shell for interactiva
  4. $ZDOTDIR/.zlogin se a shell for uma login shell
  5. $ZDOTDIR/.zlogout quando uma login shell termina normalmente

Se ZDOTDIR não estiver definido, o Zsh utiliza $HOME. As localizações dos ficheiros a nível do sistema dependem da instalação; as predefinições comuns são /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin e /etc/zlogout (manual do Zsh).

zsh -f começa com RCS não definido, pelo que normalmente ignora os ficheiros de utilizador $ZDOTDIR acima; /etc/zshenv é sempre lido e pode alterar essa opção.

Sequência de arranque do Zsh para os quatro modos de shellSequência de arranque do Zsh para os quatro modos de shell

.zprofile é executado uma vez por processo de login shell, não uma vez por início de sessão no computador. Se cada novo separador do terminal iniciar uma login shell, cada separador lê-o.

O que deve ficar em cada ficheiro

FicheiroRegra de selecçãoBoas opçõesEvitar
.zshenvTodas as shells Zsh com RCS definidoVariáveis raras necessárias a todos os processos Zshoutput, aliases, prompts, chamadas de rede, comandos lentos
.zprofileLogin shellsconfiguração do path da sessão de login e predefinições exportadashooks interactivos, completions, combinações de teclas
.zshrcShells interactivasprompt, completion, aliases, opções da shell, hooks de ferramentas interactivosoutput ou alterações destinadas a scripts
.zloginLogin shells, depois de .zshrcacções raras após o login e a interactividadeconfiguração que tenha de preceder .zshrc
.zlogoutSaída de uma login shellpequenas acções de limpeza ou reposição do terminalestado importante que tenha de sobreviver a crashes ou exec

Mantenha .zshenv aborrecido quando RCS estiver definido

Com RCS definido, todas as zsh -c não interactivas lêem .zshenv (manual do Zsh). Um echo, uma chamada a um gestor de pacotes ou um subprocesso dispendioso nesse ficheiro podem corromper o output dos comandos e tornar os scripts mais lentos. A maioria dos utilizadores precisa de pouco ou nada neste ficheiro.

Uma variável exportada em .zprofile é herdada pelos processos-filhos dessa login shell. Não é injectada automaticamente em aplicações GUI, serviços ou shells não relacionadas cujo processo-pai nunca tenha lido o ficheiro.

Utilize .zprofile para a configuração exclusiva de login

Um perfil pequeno pode estabelecer predefinições exportadas e um path idempotente:

# ~/.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 remove entradas duplicadas de arrays. Torna mais seguro fazer source repetidamente, embora os ficheiros de arranque devam continuar a evitar trabalho desnecessário.

Utilize .zshrc para o estado interactivo

# ~/.zshrc
setopt auto_cd hist_ignore_all_dups share_history

autoload -Uz compinit
compinit

alias ll='ls -lah'
bindkey -e

Aliases, widgets de completion, mapas de teclas, prompts e hooks de alteração de directório pertencem à shell interactiva que os utiliza.

Os gestores de versões ultrapassam a fronteira simples entre ficheiros

«Coloque os gestores de versões em .zprofile porque são lentos» é um conselho pouco fiável. Um gestor de versões pode emitir vários tipos de código:

  • um path ou uma variável de root estáticos
  • um path para command shim necessário aos processos-filhos
  • definições de completion
  • funções da shell que alteram a shell actual
  • hooks de alteração de directório para a troca automática de versões

As partes interactivas têm de existir em todas as shells interactivas, incluindo uma zsh aninhada iniciada a partir de um editor. A configuração actual do fnm para Zsh coloca, por isso, este hook em .zshrc:

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

A configuração actual do pyenv para Zsh coloca igualmente a inicialização interactiva completa em .zshrc:

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

O Pyenv documenta um modo pyenv init --path mais restrito para configurar o shim-path, enquanto o pyenv init completo também instala completion e funções da shell. Inspeccione a documentação actual e o output gerado por uma ferramenta antes de separar as partes.

Se o arranque for lento, meça-o antes de mover o código para um modo em que deixe de funcionar.

Não faça source do perfil inteiro a partir de .zshrc

Uma solução comum para várias plataformas é:

# Avoid this blanket coupling.
source ~/.zprofile

Isto transforma acções exclusivas de login em acções executadas por cada shell interactiva e pode repetir o arranque de agentes, o acesso ao keychain, o output ou a alteração do path.

Se tanto as shells interactivas de login como as não-login precisarem de um pequeno conjunto de exports estáticos, extraia um fragmento idempotente e faça source dele deliberadamente:

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

Este padrão mantém os dados partilhados separados dos efeitos secundários do login. Outra opção válida é configurar o terminal ou o gestor de sessões de forma consistente, em vez de suportar ambos os modos.

Diagnostique a shell que está lenta ou à qual falta estado

Reproduza directamente cada modo:

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

Meça o modo que os utilizadores realmente iniciam:

time zsh -lic exit
time zsh -ic exit

Trace a execução de ficheiros e linhas quando a origem não for clara:

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

Execute o trace numa conta de teste limpa ou inspeccione-o antes de o partilhar: os comandos de arranque podem expandir tokens e outros valores sensíveis.

Para um executável em falta, inspeccione tanto o modo como o path:

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

Conclusão

A distinção útil não é «ambiente versus aliases». É estado exclusivo de login versus estado interactivo, além da pequena quantidade de configuração que tem de afectar todos os processos Zsh quando RCS estiver definido.

Confirme as flags da shell, mantenha os ficheiros de arranque silenciosos e coloque cada hook gerado onde todas as funcionalidades necessárias estejam disponíveis. Assim, a configuração torna-se portável entre aplicações de terminal, sem fingir que o macOS e o Linux têm, cada um, um único modo fixo de arranque.

Referências