Fichiers de démarrage de 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 est 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 l’on ouvre le plus souvent sont ~/.zprofile et ~/.zshrc, mais leur nom seul ne dit pas si une ligne y a sa place. Cet article montre quels fichiers de démarrage chaque type de Zsh lit, ce qui va dans chaque fichier, et les commandes qui indiquent dans quel type de shell vous êtes.
Zsh choisit ses fichiers de démarrage selon deux propriétés indépendantes : le shell est-il un shell de connexion (login shell) et est-il interactif ? Les applications de terminal sur macOS et Linux peuvent choisir des combinaisons différentes. Inspectez donc le shell en cours au lieu de supposer que le système d’exploitation décide à votre place.
Un shell interactif sert à saisir des commandes, généralement depuis un terminal, et -i peut imposer ce mode. Un shell de connexion démarre en mode de connexion, choisi par son lanceur ou par -l ; cela ne signifie pas qu’il démarre uniquement lorsque vous ouvrez une session sur l’ordinateur (options d’état du shell Zsh).
Zsh évalue deux indicateurs indépendants
Un shell peut être :
- de connexion et interactif, comme une nouvelle fenêtre du Terminal macOS ou
zsh -l - hors connexion et interactif, comme un
zshimbriqué lancé depuis un autre shell - de connexion et non interactif, comme
zsh -lc 'command' - hors connexion et non interactif, comme
zsh -c 'command'
Le schéma place les deux indicateurs sur deux axes. Chaque case nomme les programmes qui démarrent d’ordinaire ce type de shell et indique lesquels des cinq fichiers utilisateur il lit.
Le programme qui lance le shell choisit le mode. Voici les valeurs par défaut de lanceurs courants :
| Lancé par | Mode par défaut |
|---|---|
| macOS Terminal, iTerm2 | de connexion, interactif |
| GNOME Terminal | hors connexion, interactif |
| Terminal VS Code sur macOS | de connexion, interactif (zsh -l) |
| Terminal VS Code sur Linux | hors connexion, interactif |
ssh host | de connexion, interactif |
ssh host 'command' | hors connexion, non interactif |
nouvelle fenêtre tmux, avec un default-command vide | de connexion, interactif |
zsh lancé depuis un shell | hors connexion, interactif |
zsh script.zsh | hors connexion, non interactif |
Vos réglages peuvent modifier chaque ligne. Pour voir le mode réel du shell courant, exécutez :
print -r -- "interactive=$options[interactive] login=$options[login]"
L’ordre de démarrage réel
Avec les options de démarrage par défaut, Zsh lit les fichiers globaux, puis le fichier utilisateur correspondant. L’ordre des fichiers utilisateur est :
$ZDOTDIR/.zshenvpour chaque invocation de Zsh$ZDOTDIR/.zprofilesi le shell est un shell de connexion$ZDOTDIR/.zshrcsi le shell est interactif$ZDOTDIR/.zloginsi le shell est un shell de connexion$ZDOTDIR/.zlogoutquand un shell de connexion interactif se termine normalement
Si ZDOTDIR n’est pas défini, Zsh utilise $HOME. L’emplacement des fichiers système dépend de l’installation ; les valeurs courantes sont /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin et /etc/zlogout (manuel de Zsh).
Sur macOS, /etc/zprofile exécute path_helper, qui place les répertoires système en tête de PATH. Définissez vos propres entrées PATH dans ~/.zprofile, qui s’exécute après, et non dans ~/.zshenv.
RCS est une option de Zsh, activée par défaut, qui lui permet de lire les fichiers de démarrage après /etc/zshenv. zsh -f la désactive et ignore normalement les autres fichiers de démarrage. /etc/zshenv s’exécute toujours et peut réactiver RCS. Si un problème disparaît dans ce shell, examinez la configuration de démarrage qu’il a ignorée (manuel de Zsh).
Le schéma suivant montre l’ordre complet pour une nouvelle fenêtre Terminal de macOS, fichiers globaux compris, et ce qui arrive à une entrée PATH ajoutée dans ~/.zshenv plutôt que dans ~/.zprofile.
.zprofile s’exécute une fois par processus de shell de connexion, et non une fois par ouverture de session sur l’ordinateur. Si chaque nouvel onglet de terminal démarre un shell de connexion, chaque onglet le lit.
Ce qui va dans chaque fichier
| Fichier | Règle de sélection | À y mettre | À éviter |
|---|---|---|---|
.zshenv | Tout Zsh | ZDOTDIR et les rares variables dont tout processus Zsh a besoin | sorties, alias, prompts, appels réseau, commandes lentes |
.zprofile | Shells de connexion | configuration du chemin de la session de connexion et valeurs par défaut exportées | hooks interactifs, complétions, raccourcis clavier |
.zshrc | Shells interactifs | prompt, complétion, alias, options du shell, hooks d’outils interactifs | sorties ou modifications destinées aux scripts |
.zlogin | Shells de connexion, après .zshrc | rares actions de connexion qui doivent s’exécuter après .zshrc | configuration qui doit précéder .zshrc |
.zlogout | Fin d’un shell de connexion | petit nettoyage ou réinitialisation du terminal | état important qui doit survivre aux plantages ou à exec |
Gardez .zshenv court et silencieux
Chaque zsh -c non interactif lit .zshenv (manuel de Zsh). Un echo, un appel à un gestionnaire de paquets ou un sous-processus coûteux peut y corrompre la sortie des commandes et ralentir les scripts. La plupart des utilisateurs n’ont besoin de presque rien dans ce fichier.
L’exception courante est ZDOTDIR. Définissez-le dans ~/.zshenv pour garder les autres fichiers de démarrage dans un autre répertoire, par exemple ~/.config/zsh.
Utilisez .zprofile pour la configuration réservée à la connexion
Une variable exportée dans .zprofile est héritée par les enfants de ce shell de connexion. Elle n’est pas injectée automatiquement dans des applications graphiques, des services ou des shells sans rapport, dont le parent n’a jamais lu le fichier.
Un petit profil sur macOS peut charger Homebrew, définir des valeurs par défaut exportées et construire PATH sans doublons :
# ~/.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 garde chaque entrée une seule fois. Sans lui, chaque source du fichier ou chaque shell de connexion imbriqué ajoute de nouveau le même répertoire, et echo $PATH l’affiche plusieurs fois.
Utilisez .zshrc pour l’état interactif
Une nouvelle invocation de zsh hérite des variables exportées. Elle n’hérite ni des alias, ni des fonctions, ni des options du shell, ni des raccourcis clavier, ni des hooks du parent. Chaque shell interactif doit les définir de nouveau, ce que fait .zshrc. Zsh ignore .zshrc lors des invocations non interactives normales.
Le schéma compare un zsh imbriqué et un shell de script zsh -c lancés depuis le même shell de connexion. Dans cet exemple, seul .zshrc définit l’état du shell représenté.
Un .zshrc minimal définit des options, la complétion, des alias et des raccourcis clavier :
# ~/.zshrc
setopt auto_cd hist_ignore_all_dups share_history
autoload -Uz compinit
compinit
alias ll='ls -lah'
bindkey -e
Où placer les hooks des gestionnaires de versions
« Mettez 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 chemin statique ou une variable de racine
- un chemin de shims de commandes nécessaire aux 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 deux premiers sont exportés et passent aux shells enfants. Le reste est de l’état de shell : il doit 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 de même 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 plus restreint, pyenv init --path, pour configurer le chemin des shims, tandis que pyenv init complet installe aussi la complétion et des fonctions shell. Consultez la documentation actuelle de l’outil et sa sortie générée avant de le scinder.
Si le démarrage est lent, mesurez-le avant de déplacer du code vers un mode où il ne fonctionne plus. La section sur le diagnostic explique comment.
Ne chargez pas tout le profil depuis .zshrc
Un contournement multiplateforme courant est :
# Avoid this blanket coupling.
source ~/.zprofile
Cela transforme des actions réservées à la connexion en actions exécutées pour chaque shell interactif. Cela peut relancer ssh-agent, redemander l’accès au trousseau, afficher du texte ou ajouter deux fois des entrées à PATH.
Si les shells interactifs de connexion et hors connexion ont tous besoin d’un petit ensemble d’exports statiques, extrayez un fragment idempotent et chargez-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.
Un shell de connexion lit environment.zsh par .zprofile. Un shell interactif hors connexion le lit par .zshrc. Les actions de connexion comme ssh-agent restent dans .zprofile : ce modèle sépare donc les données partagées des effets de bord de la connexion. Une autre option consiste à configurer votre terminal pour qu’il démarre un shell de connexion, de sorte que chaque onglet lise .zprofile.
Diagnostiquer le shell lent ou auquel il manque un état
Partez du symptôme : un shell lent ou un réglage manquant.
Reproduisez chaque mode directement :
zsh -lic 'print "login interactive"'
zsh -ic 'print "non-login interactive"'
zsh -lc 'print "login non-interactive"'
zsh -c 'print "non-login non-interactive"'
Pour un shell lent, utilisez time pour le total, zprof pour savoir quelle fonction prend du temps, et la trace -x pour savoir quel fichier et quelle ligne s’exécutent.
Mesurez le mode que lance votre terminal :
time zsh -lic exit
time zsh -ic exit
Chargez le profileur en haut de .zshrc et affichez son rapport à la fin. Le prochain nouveau shell affiche un tableau des fonctions triées par temps :
# first line of ~/.zshrc
zmodload zsh/zprof
# last line of ~/.zshrc
zprof
Tracez l’exécution fichier par fichier et ligne par ligne quand la source n’est pas claire :
PS4='%N:%i> ' zsh -xlic exit
Exécutez la trace dans un compte de test propre, ou relisez-la avant de la partager : les commandes de démarrage peuvent développer des jetons et d’autres valeurs sensibles.
Pour un exécutable introuvable, inspectez à la fois le mode et le chemin :
print -r -- "interactive=$options[interactive] login=$options[login]"
print -l -- $path
whence -va python node uv
Pour appliquer une modification, ouvrez un nouvel onglet ou exécutez exec zsh -l (connexion) ou exec zsh (hors connexion). source ~/.zshrc réexécute le fichier dans le shell courant et conserve tout ce que l’ancienne version avait défini.
Conclusion
La distinction utile n’est pas « environnement contre alias ». C’est l’état réservé à la connexion contre l’état interactif, plus le peu qui doit toucher chaque processus Zsh.
Quand un shell se comporte mal, exécutez la commande print de la première section, puis vérifiez que le réglage se trouve dans un fichier que ce mode lit.