Автоматический перевод
Эта статья была автоматически переведена с оригинальной английской версии.
Файлы запуска Zsh: ~/.zprofile и ~/.zshrc в macOS и Linux
Если ваш терминал кажется медленным или переменные окружения загружаются не там, где вы ожидаете, скорее всего, вы столкнулись с порядком запуска Zsh.
Разделение между ~/.zprofile и ~/.zshrc — один из самых частых источников путаницы при переходе на Zsh, особенно в macOS, где поведение по умолчанию отличается от Linux.
TL;DR
~/.zprofile предназначен для настройки окружения. Он выполняется один раз за логин, что в macOS означает один раз на вкладку терминала. Помещайте туда PATH, EDITOR и менеджеры версий вроде fnm или pyenv.
~/.zshrc предназначен для интерактивной конфигурации. Он выполняется каждый раз при запуске новой оболочки. Помещайте туда алиасы, темы prompt и привязки клавиш.
Поток запуска оболочки
Чтобы понять, куда что помещать, нужно понимать, когда загружаются файлы. У Zsh есть определенная иерархия.
Логин-оболочки и интерактивные оболочки
Логин-оболочка — это первая оболочка, которую вы получаете после аутентификации. В macOS каждая новая вкладка или окно терминала по умолчанию является логин-оболочкой. В Linux при открытии терминала обычно запускается не-логин интерактивная оболочка, и именно отсюда берется большая часть кроссплатформенной путаницы.
Интерактивная оболочка — это любая оболочка, в которой вы можете вводить команды.
Вот что реально происходит, когда вы открываете терминал в macOS:
Порядок загрузки
~/.zshenv(необязательно). Выполняется для любой оболочки, включая неинтерактивные скрипты. Не помещайте сюда вывод или тяжелую логику — это может ломать скрипты, которые подключают вашу конфигурацию. Используйте его только для переменных окружения, которые должны существовать везде, что для большинства пользователей бывает редко.~/.zprofile. Выполняется только для логин-оболочек. Ваш этап инициализации.~/.zshrc. Выполняется для интерактивных оболочек. Ваш этап кастомизации.~/.zlogin(необязательно). Выполняется в самом конце запуска логин-оболочки.
Что куда помещать?
~/.zprofile: слой окружения
Здесь настраивается все, что наследуют остальные процессы: пути, переменные, менеджеры версий языков.
Что должно быть здесь:
- Изменения
PATH. - Переменные окружения:
EDITOR,LANG,GOPATH,JAVA_HOME. - Инициализация инструментов, влияющая на окружение:
pyenv,rbenv,fnm,cargo.
Все это нужно вычислять только один раз. Если поместить это в .zshrc, оно будет пересчитываться каждый раз при открытии дочерней оболочки или запуске скрипта, что не только тратит время, но и может приводить к дублированию записей в 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: интерактивный слой
Здесь настраивается оболочка, в которой вы реально работаете.
Что должно быть здесь:
- Алиасы:
alias g='git'. - Ваш prompt: Starship, Powerlevel10k, Pure.
- Автодополнение:
compinit. - Привязки клавиш:
bindkey. - Опции оболочки:
setopt autocd,setopt histignorealldups.
Все это имеет значение только тогда, когда за клавиатурой сидит человек. Скрипту, работающему в фоне, не нужны ваш prompt или 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
Почему не положить все в один файл?
Вы можете спросить: если .zprofile выполняется первым, почему бы не поместить туда алиасы и вообще не использовать .zshrc?
Все сводится к наследованию против повторного определения.
Переменные окружения наследуются
Когда вы делаете export EDITOR="vim" в родительской оболочке, каждый дочерний процесс наследует это значение: дочерние оболочки, скрипты, программы. Вы задаете его один раз, и оно распространяется вниз по дереву процессов, поэтому .zprofile — правильное место для export.
Алиасы и функции — нет
Алиасы вроде alias g='git' и функции оболочки локальны для текущей оболочки. Они не передаются дочерним оболочкам.
Если вы определите алиас в .zprofile, он будет существовать в вашей верхнеуровневой логин-оболочке. Как только вы вводите zsh, чтобы запустить дочернюю оболочку, или запускаете скрипт, этот алиас исчезает. Чтобы алиасы были доступны везде, их нужно определять заново в каждой новой интерактивной оболочке. Именно для этого и нужен .zshrc.
Скриптам не нужны пользовательские интерактивные функции
Когда вы запускаете shell-скрипт (./deploy.sh), стартует новая неинтерактивная оболочка. Ей не нужен ваш prompt, не нужны git-алиасы, и уж точно она не хочет ждать, пока загрузится oh-my-zsh. Если держать интерактивную конфигурацию в .zshrc, скрипты будут выполняться быстро и предсказуемо, без утечки ваших персональных настроек.
Частые ошибки
Помещать nvm или pyenv в .zshrc
Вы открываете новую вкладку терминала, и проходит две-три секунды, прежде чем можно что-то вводить. Менеджеры версий обычно имеют тяжелую инициализацию, а .zshrc выполняется каждый раз без исключения. Перенесите их в ~/.zprofile, и лаг исчезнет.
Растущий PATH
В вашем $PATH одни и те же директории оказываются перечислены по пять раз. Причина почти всегда в том, что export PATH="$HOME/bin:$PATH" находится в .zshrc: при каждой перезагрузке (source ~/.zshrc) или запуске дочерней оболочки путь снова добавляется. Перенесите определения PATH в ~/.zprofile.
Перезагрузка после изменений
Изменения в ~/.zprofile не применяются к текущей оболочке, потому что .zprofile читается только при логине. Вы можете либо закрыть вкладку и открыть новую (самый простой вариант), либо вручную выполнить source ~/.zprofile.
Для .zshrc просто:
source ~/.zshrc
Конфигурация, которая работает и в macOS, и в Linux
Если вы переключаетесь между машинами (macOS на работе, Linux дома, или наоборот), вы столкнетесь с одной особенностью. Терминалы в Linux часто запускаются как не-логин оболочки, а значит, полностью пропускают ~/.zprofile.
Обычный обходной путь — подключать .zprofile из .zshrc, если он еще не был загружен:
# ~/.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
Итоги
| File | Назначение | Примеры |
|---|---|---|
~/.zshenv |
Критичные переменные окружения | ZDOTDIR (только для продвинутых пользователей) |
~/.zprofile |
Настройка окружения | PATH, EDITOR, eval "$(pyenv init -)" |
~/.zshrc |
Интерактивная конфигурация | alias, prompt, bindkey, compinit |
Эту тему в основном покрывают два правила. Переменные наследуются вниз по дереву процессов, поэтому export должно находиться в .zprofile. Алиасы и функции не наследуются, поэтому они должны быть в .zshrc и переопределяться для каждой новой интерактивной оболочки. Если новые вкладки открываются медленно, виновник почти всегда — тяжелый pyenv init или nvm.sh, помещенный в .zshrc вместо .zprofile.