Перейти к содержанию

Автоматический перевод

Эта статья была автоматически переведена с оригинальной английской версии.

Файлы запуска Zsh: ~/.zprofile и ~/.zshrc в macOS и Linux

Если ваш терминал кажется медленным или переменные окружения загружаются не там, где вы ожидаете, скорее всего, вы столкнулись с порядком запуска Zsh.

Разделение между ~/.zprofile и ~/.zshrc — один из самых частых источников путаницы при переходе на Zsh, особенно в macOS, где поведение по умолчанию отличается от Linux.

TL;DR

~/.zprofile предназначен для настройки окружения. Он выполняется один раз за логин, что в macOS означает один раз на вкладку терминала. Помещайте туда PATH, EDITOR и менеджеры версий вроде fnm или pyenv.

~/.zshrc предназначен для интерактивной конфигурации. Он выполняется каждый раз при запуске новой оболочки. Помещайте туда алиасы, темы prompt и привязки клавиш.

Структура конфигурации Zsh


Поток запуска оболочки

Чтобы понять, куда что помещать, нужно понимать, когда загружаются файлы. У Zsh есть определенная иерархия.

Логин-оболочки и интерактивные оболочки

Логин-оболочка — это первая оболочка, которую вы получаете после аутентификации. В macOS каждая новая вкладка или окно терминала по умолчанию является логин-оболочкой. В Linux при открытии терминала обычно запускается не-логин интерактивная оболочка, и именно отсюда берется большая часть кроссплатформенной путаницы.

Интерактивная оболочка — это любая оболочка, в которой вы можете вводить команды.

Вот что реально происходит, когда вы открываете терминал в macOS:

Поток запуска Zsh

Порядок загрузки

  1. ~/.zshenv (необязательно). Выполняется для любой оболочки, включая неинтерактивные скрипты. Не помещайте сюда вывод или тяжелую логику — это может ломать скрипты, которые подключают вашу конфигурацию. Используйте его только для переменных окружения, которые должны существовать везде, что для большинства пользователей бывает редко.
  2. ~/.zprofile. Выполняется только для логин-оболочек. Ваш этап инициализации.
  3. ~/.zshrc. Выполняется для интерактивных оболочек. Ваш этап кастомизации.
  4. ~/.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.