Fichiers de démarrage Zsh : .zprofile ou .zshrc sur macOS et Linux

Traduction automatique Cet article a été traduit automatiquement depuis la version originale en anglais.

Si votre terminal semble lent, ou si une variable d’environnement apparaît dans un terminal mais pas dans un autre, la cause est souvent le mode de démarrage de Zsh. Les deux fichiers que la plupart des utilisateurs consultent sont ~/.zprofile et ~/.zshrc, mais aucun nom de fichier ne suffit à déterminer à lui seul où placer une ligne.

Zsh choisit ses fichiers de démarrage selon deux propriétés indépendantes : le shell est-il un login shell et est-il interactif ? Les applications de terminal sur macOS et Linux peuvent choisir différentes combinaisons ; inspectez donc le shell en cours d’exécution au lieu de supposer que le système d’exploitation décide pour vous.

En bref. Placez l’initialisation de la session de connexion dans .zprofile et le comportement interactif dans .zshrc. Gardez .zshenv silencieux et minimal lorsque RCS est défini. Ne déplacez pas les hooks interactifs des gestionnaires de versions dans .zprofile et ne sourcez pas l’intégralité du profile depuis .zshrc ; inspectez le code généré par l’outil et suivez ses instructions Zsh à jour.

Zsh évalue deux flags indépendants

Fichiers de démarrage Zsh sélectionnés selon les modes login et interactifFichiers de démarrage Zsh sélectionnés selon les modes login et interactif

Un shell peut être :

  • login et interactif, lorsque Apple Terminal est configuré pour ouvrir le login shell par défaut du compte
  • non-login et interactif, comme un zsh imbriqué ou dans de nombreuses configurations de terminaux Linux
  • login et non interactif, comme zsh -lc 'command'
  • non-login et non interactif, comme zsh -c 'command'

Les applications de terminal, les IDE, les sessions distantes, les multiplexeurs et les flags explicites du shell peuvent choisir des modes différents. Le système d’exploitation seul ne suffit donc pas à déterminer le résultat.

Demandez directement au shell en cours d’exécution au lieu de deviner :

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

L’ordre réel de démarrage

Zsh lit les fichiers globaux, puis le fichier utilisateur correspondant. Avec les options RCS par défaut, l’ordre des fichiers utilisateur est le suivant :

  1. $ZDOTDIR/.zshenv à chaque invocation de Zsh
  2. $ZDOTDIR/.zprofile si le shell est un login shell
  3. $ZDOTDIR/.zshrc si le shell est interactif
  4. $ZDOTDIR/.zlogin si le shell est un login shell
  5. $ZDOTDIR/.zlogout lorsqu’un login shell se termine normalement

Si ZDOTDIR n’est pas défini, Zsh utilise $HOME. Les emplacements des fichiers à l’échelle du système dépendent de l’installation ; les valeurs par défaut courantes sont /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin et /etc/zlogout (manuel de Zsh).

zsh -f démarre avec RCS non défini ; il ignore donc normalement les fichiers utilisateur $ZDOTDIR ci-dessus. /etc/zshenv est toujours lu et peut modifier cette option.

Séquence de démarrage de Zsh pour les quatre modes de shellSéquence de démarrage de Zsh pour les quatre modes de shell

.zprofile est exécuté une fois par processus de login shell, et non une fois par connexion de l’utilisateur à l’ordinateur. Si chaque nouvel onglet de terminal démarre un login shell, chaque onglet le lit.

Que placer dans chaque fichier

FichierRègle de sélectionBons candidatsÀ éviter
.zshenvChaque Zsh avec RCS définiVariables rares requises par chaque processus Zshsortie, aliases, prompts, appels réseau, commandes lentes
.zprofileLogin shellsconfiguration du PATH de la session de connexion et valeurs par défaut exportéeshooks interactifs, complétions, raccourcis clavier
.zshrcShells interactifsprompt, complétion, aliases, options du shell, hooks d’outils interactifssortie ou mutations destinées aux scripts
.zloginLogin shells, après .zshrcrares actions de connexion post-interactivesconfiguration qui doit précéder .zshrc
.zlogoutFin du login shellpetites opérations de nettoyage ou de réinitialisation du terminalétat important qui doit survivre aux plantages ou à exec

Gardez .zshenv minimal lorsque RCS est défini

Lorsque RCS est défini, chaque zsh -c non interactif lit .zshenv (manuel de Zsh). Un echo, un appel à un gestionnaire de paquets ou un sous-processus coûteux à cet endroit peut corrompre la sortie des commandes et ralentir les scripts. La plupart des utilisateurs n’ont besoin de presque rien dans ce fichier.

Une variable exportée dans .zprofile est héritée par les processus enfants de ce login shell. Elle n’est pas automatiquement injectée dans des applications graphiques, des services ou des shells indépendants dont le parent n’a jamais lu le fichier.

Utilisez .zprofile pour la configuration propre aux sessions de connexion

Un profile minimal peut définir des valeurs par défaut exportées ainsi qu’un PATH idempotent :

# ~/.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 supprime les entrées en double dans les tableaux. Cela rend le sourcing répété plus sûr, même si les fichiers de démarrage doivent toujours éviter le travail inutile.

Utilisez .zshrc pour l’état interactif

# ~/.zshrc
setopt auto_cd hist_ignore_all_dups share_history

autoload -Uz compinit
compinit

alias ll='ls -lah'
bindkey -e

Les aliases, widgets de complétion, raccourcis clavier, prompts et hooks de changement de répertoire appartiennent au shell interactif qui les utilise.

Les gestionnaires de versions dépassent la simple séparation entre fichiers

Dire « placez les gestionnaires de versions dans .zprofile parce qu’ils sont lents » est un conseil peu fiable. Un gestionnaire de versions peut produire plusieurs types de code :

  • un PATH statique ou une variable racine
  • un chemin vers un shim de commande requis par les processus enfants
  • des définitions de complétion
  • des fonctions shell qui modifient le shell courant
  • des hooks de changement de répertoire pour le changement automatique de version

Les éléments interactifs doivent exister dans chaque shell interactif, y compris un zsh imbriqué lancé depuis un éditeur. La configuration Zsh actuelle de fnm place donc ce hook dans .zshrc :

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

La configuration Zsh actuelle de pyenv place elle aussi l’initialisation interactive complète dans .zshrc :

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

Pyenv documente un mode pyenv init --path plus ciblé pour configurer le chemin vers les shims, tandis que le pyenv init complet installe également la complétion et les fonctions shell. Consultez la documentation actuelle de l’outil et inspectez sa sortie générée avant de séparer ces éléments.

Si le démarrage est lent, mesurez-le avant de déplacer du code vers un mode dans lequel il ne fonctionnera plus.

Ne sourcez pas l’intégralité du profile depuis .zshrc

Une solution de contournement courante et portable entre les plateformes consiste à faire ceci :

# Avoid this blanket coupling.
source ~/.zprofile

Cela transforme les actions propres aux sessions de connexion en actions exécutées dans chaque shell interactif et peut répéter le démarrage d’un agent, l’accès au trousseau, la sortie ou la modification du PATH.

Si les shells interactifs login et non-login ont tous deux besoin d’un petit ensemble d’exports statiques, extrayez un fragment idempotent et sourcez-le explicitement :

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

Ce pattern sépare les données partagées des effets de bord propres aux sessions de connexion. Une autre option valide consiste à configurer le terminal ou le gestionnaire de sessions de manière cohérente, plutôt qu’à prendre en charge les deux modes.

Diagnostiquez le shell lent ou dépourvu de certains états

Reproduisez directement chaque mode :

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

Mesurez le mode réellement lancé par les utilisateurs :

time zsh -lic exit
time zsh -ic exit

Tracez l’exécution des fichiers et des lignes lorsque l’origine du problème n’est pas claire :

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

Exécutez la trace dans un compte de test propre ou inspectez-la avant de la partager : les commandes de démarrage peuvent développer des tokens et d’autres valeurs sensibles.

Pour un exécutable introuvable, inspectez à la fois le mode et le PATH :

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

Conclusion

La distinction utile n’est pas « environnement contre aliases ». Il s’agit de l’état propre aux sessions de connexion par opposition à l’état interactif, ainsi que de la petite quantité de configuration qui doit affecter chaque processus Zsh lorsque RCS est défini.

Confirmez les flags du shell, gardez les fichiers de démarrage silencieux et placez chaque hook généré là où toutes ses fonctionnalités requises sont disponibles. La configuration devient ainsi portable entre les applications de terminal, sans prétendre que macOS et Linux possèdent chacun un mode de lancement unique et fixe.

Références