Saltar a contenido

Traducción automática

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

Archivos de inicio de Zsh: ~/.zprofile vs ~/.zshrc en macOS y Linux

Si tu terminal va lenta, o tus variables de entorno no se cargan donde esperas, probablemente te estés encontrando con el orden de arranque de Zsh.

La separación entre ~/.zprofile y ~/.zshrc es una de las fuentes de confusión más habituales al pasarte a Zsh, especialmente en macOS, donde el comportamiento por defecto es distinto al de Linux.

TL;DR

~/.zprofile es para la configuración del entorno. Se ejecuta una vez por login, lo que en macOS significa una vez por pestaña de terminal. Pon ahí tu PATH, EDITOR y gestores de versiones como fnm o pyenv.

~/.zshrc es para la configuración interactiva. Se ejecuta cada vez que inicias un shell nuevo. Pon ahí alias, temas del prompt y atajos de teclado.

Estructura de configuración de Zsh


El flujo de arranque del shell

Para saber dónde poner cada cosa, necesitas saber cuándo se cargan los archivos. Zsh tiene una jerarquía concreta.

Shells de login vs. interactivos

Un shell de login es el primer shell que obtienes tras autenticarte. En macOS, cada nueva pestaña o ventana del terminal es, por defecto, un shell de login. En Linux, abrir un terminal suele iniciar en su lugar un shell interactivo sin login, y de ahí viene gran parte de la confusión multiplataforma.

Un shell interactivo es cualquier shell en el que puedes escribir comandos.

Esto es lo que ocurre realmente cuando abres un terminal en macOS:

Flujo de arranque de Zsh

El orden de carga

  1. ~/.zshenv (opcional). Se ejecuta para cada shell, incluidos los scripts no interactivos. No pongas aquí salida ni lógica pesada: puede romper scripts que hagan source de tu configuración. Úsalo solo para variables de entorno que deban existir en todas partes, algo poco habitual para la mayoría de la gente.
  2. ~/.zprofile. Se ejecuta solo para shells de login. Tu fase de preparación.
  3. ~/.zshrc. Se ejecuta para shells interactivos. Tu fase de personalización.
  4. ~/.zlogin (opcional). Se ejecuta al final del arranque de un shell de login.

¿Qué va en cada sitio?

~/.zprofile: la capa de entorno

Aquí es donde configuras las cosas que luego heredan todos los demás procesos: rutas, variables, gestores de versiones de lenguaje.

Qué va aquí:

  • Modificaciones de PATH.
  • Variables de entorno: EDITOR, LANG, GOPATH, JAVA_HOME.
  • Inicialización de herramientas que tocan el entorno: pyenv, rbenv, fnm, cargo.

Estas cosas solo hay que calcularlas una vez. Si las pones en .zshrc, se vuelven a calcular cada vez que abres un sub-shell o ejecutas un script, lo que no solo hace perder tiempo, sino que también puede dejar entradas duplicadas en tu PATH.

# ~/.zprofile

# 1. Set up your PATH
# Local bin first so your tools override system ones
export PATH="$HOME/.local/bin:/opt/homebrew/bin:$PATH"

# 2. Global variables
export EDITOR="nvim"
export VISUAL="nvim"
export LANG="en_US.UTF-8"

# 3. Version managers (the heavy ones)
# Doing this here keeps shell startup fast
eval "$(fnm env --use-on-cd)"
eval "$(pyenv init -)"

~/.zshrc: la capa interactiva

Aquí es donde personalizas el shell en el que realmente escribes.

Qué va aquí:

  • Alias: alias g='git'.
  • Tu prompt: Starship, Powerlevel10k, Pure.
  • Autocompletado: compinit.
  • Atajos de teclado: bindkey.
  • Opciones del shell: setopt autocd, setopt histignorealldups.

Estas cosas solo importan cuando hay una persona delante del teclado. Un script ejecutándose en segundo plano no necesita tu prompt ni tus alias de git.

# ~/.zshrc

# 1. Prompt
autoload -Uz promptinit && promptinit
prompt pure

# 2. Aliases
alias ll='ls -lah'
alias g='git'
alias gs='git status'

# 3. Shell options
setopt autocd              # cd by typing the directory name
setopt histignorealldups   # skip duplicate history entries
setopt share_history       # share history across tabs

# 4. Completions
autoload -Uz compinit && compinit

¿Por qué no ponerlo todo en un solo archivo?

Puede que te estés preguntando: si .zprofile se ejecuta primero, ¿por qué no poner ahí tus alias y olvidarte por completo de .zshrc?

La respuesta está en la herencia frente a la redefinición.

Las variables de entorno se heredan

Cuando haces export EDITOR="vim" en un shell padre, todos los procesos hijo lo heredan: sub-shells, scripts, programas. Lo defines una vez y se propaga por todo el árbol, por eso .zprofile es el sitio correcto para export.

Los alias y las funciones no

Alias como alias g='git' y las funciones de shell son locales al shell actual. No se pasan a los shells hijo.

Si defines un alias en .zprofile, existe en tu shell de login de nivel superior. En cuanto escribes zsh para iniciar un sub-shell, o ejecutas un script, ese alias desaparece. Para tener alias en todas partes, tienes que redefinirlos en cada shell interactivo nuevo. Eso es exactamente para lo que sirve .zshrc.

Los scripts no necesitan funciones pensadas para humanos

Cuando ejecutas un script de shell (./deploy.sh), se inicia un shell nuevo no interactivo. No necesita tu prompt, no necesita tus alias de git y, desde luego, no quiere esperar a que oh-my-zsh termine de cargar. Mantener la configuración interactiva en .zshrc hace que los scripts se ejecuten rápido y de forma limpia, sin que se cuele tu personalización personal.


Errores habituales

Poner nvm o pyenv en .zshrc

Abres una pestaña nueva del terminal y tarda dos o tres segundos antes de que puedas escribir nada. Los gestores de versiones suelen tener una inicialización pesada, y .zshrc se ejecuta todas y cada una de las veces. Muévelos a ~/.zprofile y el retraso desaparece.

Un PATH que no para de crecer

Tu $PATH acaba con los mismos directorios listados cinco veces. La causa casi siempre es export PATH="$HOME/bin:$PATH" metido en .zshrc: cada recarga (source ~/.zshrc) o sub-shell vuelve a añadir la ruta. Mueve las definiciones de PATH a ~/.zprofile.

Recargar después de los cambios

Los cambios en ~/.zprofile no se aplican al shell actual, porque .zprofile solo se lee al hacer login. Puedes cerrar la pestaña y abrir una nueva (la opción más sencilla) o ejecutar source ~/.zprofile manualmente.

Para .zshrc, simplemente:

source ~/.zshrc

Una configuración que funciona en macOS y Linux

Si alternas entre máquinas (macOS en el trabajo, Linux en casa, o al revés), te encontrarás con un matiz. Los terminales de Linux suelen iniciarse como shells sin login, lo que significa que se saltan ~/.zprofile por completo.

La solución habitual es hacer source de .zprofile desde .zshrc cuando todavía no se ha cargado:

# ~/.zshrc

# On Linux/non-login shells, make sure the environment is set
if [[ -o interactive && ! -o login ]]; then
    [[ -f ~/.zprofile ]] && source ~/.zprofile
fi

# ... rest of your interactive config

Resumen

File Propósito Ejemplos
~/.zshenv Variables de entorno críticas ZDOTDIR (solo usuarios avanzados)
~/.zprofile Configuración del entorno PATH, EDITOR, eval "$(pyenv init -)"
~/.zshrc Configuración interactiva alias, prompt, bindkey, compinit

Dos reglas cubren casi todo esto. Las variables se heredan hacia abajo por el árbol de procesos, así que export va en .zprofile. Los alias y las funciones no se heredan, así que van en .zshrc y hay que redefinirlos para cada shell interactivo nuevo. Si las pestañas nuevas van lentas, el culpable casi siempre es un pyenv init o nvm.sh pesado metido en .zshrc en lugar de en .zprofile.