Archivos de inicio de Zsh: .zprofile frente a .zshrc en macOS y Linux

Traducción automática Este artículo se tradujo automáticamente a partir de la versión original en inglés.

Si el terminal va lento o una variable de entorno aparece en un terminal pero no en otro, la causa suele estar en el modo de inicio de Zsh. Los dos archivos que más se suelen utilizar son ~/.zprofile y ~/.zshrc, pero el nombre del archivo, por sí solo, no indica dónde debe ir cada línea.

Zsh selecciona los archivos de inicio a partir de dos propiedades independientes: si el shell es un login shell y si es interactivo. Las aplicaciones de terminal de macOS y Linux pueden elegir combinaciones distintas, así que debes inspeccionar el shell en ejecución en lugar de asumir que lo decide el sistema operativo.

TL;DR. Pon la inicialización de la sesión de login en .zprofile y el comportamiento interactivo en .zshrc. Mantén .zshenv silencioso y minimalista cuando RCS esté definido. No muevas los hooks interactivos del gestor de versiones a .zprofile ni cargues el perfil completo desde .zshrc; inspecciona el código generado por la herramienta y sigue sus instrucciones actuales para Zsh.

Zsh evalúa dos flags independientes

Archivos de inicio de Zsh seleccionados según los modos login e interactivoArchivos de inicio de Zsh seleccionados según los modos login e interactivo

Un shell puede ser:

  • login e interactivo, cuando Apple Terminal está configurado para abrir el shell de login predeterminado de la cuenta
  • no-login e interactivo, como un zsh anidado o como sucede en muchas configuraciones de terminal de Linux
  • login y no interactivo, como zsh -lc 'command'
  • no-login y no interactivo, como zsh -c 'command'

Las aplicaciones de terminal, los IDE, las sesiones remotas, los multiplexores y los flags explícitos del shell pueden elegir modos distintos, por lo que el sistema operativo, por sí solo, no determina el resultado.

Pregúntaselo al shell en ejecución en lugar de hacer suposiciones:

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

El orden de inicio real

Zsh lee los archivos globales y después el archivo de usuario correspondiente. Con las opciones predeterminadas de RCS, el orden de los archivos de usuario es:

  1. $ZDOTDIR/.zshenv en cada invocación de Zsh
  2. $ZDOTDIR/.zprofile si el shell es un login shell
  3. $ZDOTDIR/.zshrc si el shell es interactivo
  4. $ZDOTDIR/.zlogin si el shell es un login shell
  5. $ZDOTDIR/.zlogout cuando un login shell termina con normalidad

Si ZDOTDIR no está definido, Zsh utiliza $HOME. Las ubicaciones de los archivos de todo el sistema dependen de la instalación; los valores predeterminados habituales son /etc/zshenv, /etc/zprofile, /etc/zshrc, /etc/zlogin y /etc/zlogout (manual de Zsh).

zsh -f comienza con RCS sin definir, por lo que normalmente omite los archivos de usuario $ZDOTDIR anteriores; /etc/zshenv siempre se lee y puede modificar esa opción.

Secuencia de inicio de Zsh para cuatro modos de shellSecuencia de inicio de Zsh para cuatro modos de shell

.zprofile se ejecuta una vez por cada proceso de login shell, no una vez por cada inicio de sesión en el equipo. Si cada pestaña nueva del terminal inicia un login shell, todas las pestañas lo leen.

Qué corresponde a cada archivo

ArchivoCriterio de selecciónBuenos candidatosEvitar
.zshenvCada Zsh con RCS establecidoVariables poco frecuentes necesarias para todos los procesos de Zshsalida, alias, prompts, llamadas de red, comandos lentos
.zprofileShells de inicio de sesiónconfiguración de rutas de la sesión de inicio de sesión y valores predeterminados exportadoshooks interactivos, completado, combinaciones de teclas
.zshrcShells interactivosprompt, completado, alias, opciones del shell, hooks de herramientas interactivassalida o mutaciones destinadas a scripts
.zloginShells de inicio de sesión, después de .zshrcacciones poco frecuentes posteriores al inicio de sesión interactivoconfiguración que debe preceder a .zshrc
.zlogoutSalida de un shell de inicio de sesiónpequeñas acciones de limpieza o restablecimiento del terminalestado importante que debe sobrevivir a fallos o a exec

Mantén .zshenv sin complicaciones cuando RCS esté establecido

Con RCS establecido, cada zsh -c no interactivo lee .zshenv (manual de Zsh). Un echo, una llamada al gestor de paquetes o un subproceso costoso ahí pueden corromper la salida de los comandos y ralentizar los scripts. La mayoría de los usuarios necesitan poco o nada en este archivo.

Una variable exportada en .zprofile se hereda en los procesos hijos de ese shell de inicio de sesión. No se inyecta automáticamente en aplicaciones gráficas, servicios o shells independientes cuyo proceso padre nunca haya leído el archivo.

Usa .zprofile para la configuración exclusiva del inicio de sesión

Un perfil pequeño podría establecer valores predeterminados exportados y una ruta 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 elimina las entradas duplicadas de un array. Esto hace más seguro cargar el archivo varias veces, aunque los archivos de inicio deberían seguir evitando trabajo innecesario.

Usa .zshrc para el estado interactivo

# ~/.zshrc
setopt auto_cd hist_ignore_all_dups share_history

autoload -Uz compinit
compinit

alias ll='ls -lah'
bindkey -e

Los alias, widgets de completado, mapas de teclas, prompts y hooks de cambio de directorio pertenecen al shell interactivo que los utiliza.

Los gestores de versiones traspasan los límites de la división sencilla por archivos

«Pon los gestores de versiones en .zprofile porque son lentos» es un consejo poco fiable. Un gestor de versiones puede emitir varios tipos de código:

  • una ruta estática o una variable raíz
  • la ruta a un command shim que necesitan los procesos hijos
  • definiciones de completado
  • funciones del shell que cambian el shell actual
  • hooks de cambio de directorio para cambiar automáticamente de versión

Las partes interactivas deben existir en todos los shells interactivos, incluido un zsh anidado iniciado desde un editor. Por eso, la configuración actual de fnm para Zsh coloca este hook en .zshrc:

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

La configuración actual de pyenv para Zsh también incluye una inicialización interactiva completa en .zshrc:

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

La documentación de pyenv describe un modo pyenv init --path más limitado para configurar la ruta de los shims, mientras que pyenv init también instala el autocompletado y las funciones del shell. Consulta la documentación actual y la salida generada por cada herramienta antes de dividir su configuración.

Si el arranque es lento, mídelo antes de trasladar código a un modo en el que deje de funcionar.

No cargues el perfil completo desde .zshrc

Una solución habitual para distintas plataformas es:

# Avoid this blanket coupling.
source ~/.zprofile

Esto convierte las acciones exclusivas del login en acciones que se ejecutan en cada shell interactivo, y puede repetir el arranque del agente, el acceso al llavero, la salida por pantalla o la modificación de la ruta.

Si tanto los shells interactivos de login como los que no lo son necesitan un pequeño conjunto de exports estáticos, extrae un fragmento idempotente y cárgalo de forma explícita:

# ~/.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 patrón mantiene separados los datos compartidos y los efectos secundarios exclusivos del login. Otra opción válida es configurar de forma coherente el terminal o el gestor de sesiones, en lugar de admitir ambos modos.

Diagnostica el shell que es lento o carece de estado

Reproduce cada modo directamente:

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

Mide el modo que los usuarios ejecutan realmente:

time zsh -lic exit
time zsh -ic exit

Traza la ejecución de archivos y líneas cuando no esté claro qué se carga:

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

Ejecuta la traza en una cuenta de prueba limpia o revísala antes de compartirla: los comandos de arranque pueden expandir tokens y otros valores sensibles.

Si falta un ejecutable, inspecciona tanto el modo como la ruta:

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

Conclusión

La distinción útil no es «entorno frente a alias». Es el estado exclusivo del login frente al estado interactivo, además de la pequeña cantidad de configuración que debe afectar a todos los procesos de Zsh cuando está definido RCS.

Confirma los flags del shell, mantén silenciosos los archivos de arranque y coloca cada hook generado donde estén disponibles todas las funciones que necesita. Así, la configuración será portable entre aplicaciones de terminal, sin asumir que macOS y Linux tienen un único modo fijo de lanzamiento.

Referencias