Сайт FSA
03.10.2025

zsh

ZSH — это современная командная оболочка UNIX. Она совместима с командной оболочкой bourne shell, которую часто называют sh, по имени исполняемого файла. Лично я столкнулся с ней, когда заинтересовался небольшим удобством для себя — отображением имени ветки в репозиториях git. Одним из решением было установка оболочки zsh с набором плагинов «Oh My Zsh» от сообщества. В его составе уже сейчас больше 300 плагинов и больше 140 тем. Набор замечательный, и я пользовался им продолжительное время. Но немного напрягало, что это большой по объёму код, куда можно спрятать что угодно. Кроме этого, набор способен самостоятельно обновляться, хоть это и отключается. Внутренний параноик был против этого, поэтому я решил разобраться как можно обойтись без набора плагинов простой конфигурацией zsh, тем более, что пользовался я очень небольшой частью набора. В таком варианте это и работает быстрее.

Постановка задачи

Прежде всего, что мне необходимо от командной строки из того, что я реально использовал в «Oh My Zsh»:

  1. навигация по файловой системе без команды cd;
  2. строка-приглашение в стиле eastwood: отображение текущего пути, вспомогательной информации о git репозитории;
  3. короткие алиасы для часто используемых команд;
  4. короткие алиасы для часто используемых директорий;
  5. поиск по истории после ввода нескольких символов в командной строке (а не перебор всей истории команд).

Конфигурация по умолчанию

Для начала я очистил файл конфигурации от всех комментариев и оставил конфигурацию по умолчанию. За одно добавил опцию для удобной навигации по файловой системе, когда не нужно вводить команду cd, а можно просто набрать имя любой папки. Бонусом, ввод имени папки не сохраняется в истории как вереница команд cd.

autoload -Uz compinit
compinit

setopt COMPLETE_IN_WORD
setopt AUTOCD

Работа с историей команд

Далее включил историю и настроил её:

HISTFILE=$HOME/.zsh_history

setopt APPEND_HISTORY       # дописывать историю в файл
setopt INC_APPEND_HISTORY   # сразу записывать команду в файл
setopt HIST_IGNORE_DUPS     # не сохранять дубликаты подряд
setopt HIST_IGNORE_SPACE    # не сохранять команды с пробелом в начале
setopt HIST_VERIFY          # подтверждать перед выполнением !history

Плагины для удобства

Из имеющихся в репозиториях плагинов в Ubuntu и Debian нашёл:

Второй плагин добавляет функционал, которого у меня изначально не было, но я решил его оставить.

После установки плагинов через apt или dnf добавил их подключение. Чтобы не было ошибок в том случае, если плагины не установлены, добавил проверку их наличия перед загрузкой. Обязательно нужно обратить внимание на порядок подключения. Если поставить zsh-autosuggestions после zsh-syntax-highlighting, то могут возникать глюки.

if [ -f /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh ]; then
  source /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh
fi
if [ -f /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh ]; then
  source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
fi

К сожалению не нашлось удобного плагина, который позволяет искать в истории аналогичные команды с помощью стрелок вверх и вниз при вводе части команды, поэтому установил его из git репозитория:

git clone https://github.com/zsh-users/zsh-history-substring-search ~/.zsh/zsh-history-substring-search

Установка производится в домашнюю папку пользователя. Подключаю плагин, предварительно проверив его наличие:

if [ -f $HOME/.zsh/zsh-history-substring-search/zsh-history-substring-search.zsh ]; then
  source $HOME/.zsh/zsh-history-substring-search/zsh-history-substring-search.zsh

  bindkey '^[[A' history-substring-search-up
  bindkey '^[[B' history-substring-search-down
fi

Настройка строки-приглашения

Далее настроил строку-приглашения. Поддержка zsh есть непосредственно в git. Добавляем строки согласно рекомендации и настраиваю приглашение по собственному желанию. Также тут присутствует функция git_status_symbol, которая позволяет добавить в приглашение красную звёздочку если в текущей директории есть git репозиторий с изменёнными файлами, а также user_host_ssh, которая отображает имя пользователя и пароль, если пользователь зашёл в систему через ssh или переключился на другого пользователя.

git_status_symbol() {
  if git rev-parse --is-inside-work-tree &> /dev/null; then
    if ! git diff --quiet &> /dev/null; then
      echo "%F{red}*%f"
    fi
  fi
}
user_host_ssh() {
  if [[ -n "$SSH_CONNECTION" || -n "$SUDO_USER" ]]; then
    echo '%F{red}%n@%m%f:'
  fi
}
autoload -Uz vcs_info
precmd() { vcs_info }
zstyle ':vcs_info:git:*' formats '[%b]'
setopt PROMPT_SUBST
PROMPT='$(user_host_ssh)$(git_status_symbol)%F{green}${vcs_info_msg_0_}%F{cyan}[%~]%f%(#.#.$) '

Здесь в PROMPT:

Я решил за одно добавить в приглашении имя пользователя и короткое имя машины, если вдруг решусь устанавливать zsh на удалённые машины и использовать его, так будет понятнее с какой машиной я работаю. Локально у себя на компьютере лучше убрать, чтобы сэкономить место на экране, как это сделано в оригинальной теме eastwood.

Вместо условного оператора можно использовать просто %#, который аналогично выведет # для root и % для других пользователей.

Стиль вывода информации о git репозитории можно настроить в строке с zstyle.

Алиасы для команд

Далее добавил алиасы для часто используемых команд. Выбрал то, что нужно мне. Вообще можно поискать и другие в папке plugins в «Oh My Zsh». Для добавления алиаса нужно добавить команду alias с указанием сокращения или функцию, если требуется выполнить что-то сложнее одной команды. Во втором случае, для примера привожу вариант команд gpa и gpat, которые находят все настроенные удалённые репозитории в репозитории git и делают команды push и push с тегами во все из них. Пробовал предложить эти команды в «Oh My Zsh», но мой запрос отклонили, но это не мешает добавить эти команды в свой файл конфигурации.

alias ga='git add'
alias gaa='git add --all'
alias gc='git commit --verbose'
alias gc!='git commit --verbose --amend'
alias gca='git commit --verbose --all'
alias gca!='git commit --verbose --all --amend'
alias glog='git log --oneline --decorate --graph'
function gpa() {
    for server in $(git remote -v | cut -f1 | uniq) ; do
        echo "git push $server"; git push $server
    done
}
function gpat() {
    for server in $(git remote -v | cut -f1 | uniq) ; do
        echo "git push $server --tags"; git push $server --tags
    done
}
alias gst='git status'

Для vscode пришлось немного исправить код, но можно было бы и ввести переменную, как в оригинале:

function vsc {
  if (( $# )); then
    code $@
  else
    code .
  fi
}

Осталось настроить сокращения для часто используемых путей, например www для папки /var/www. В этом случае достаточно будет ввести в командной строке ~www, чтобы попасть в необходимую папку.

hash -d www=/var/www

При переходе в папки, которые указаны в hash, также экономится пространство с строке приглашения, т.к. вместо полного пути отображается его сокращённый вариант. Особенно это удобно, если нужно сократить длинный путь, например, если добавить следующее

hash -d tavda=/home/user/NetBeansProjects/tavda.info

то при входе в папку /home/user/NetBeansProjects/tavda.info в строке приглашении высветится ~tavda, а для вложенных папок будет отображён сокращённый путь, например, ~tavda/public.

Заключение

В результате получился вот такой файл конфигурации:

# default config
autoload -Uz compinit
compinit
setopt COMPLETE_IN_WORD
setopt AUTOCD

# history
HISTFILE=$HOME/.zsh_history

setopt APPEND_HISTORY
setopt INC_APPEND_HISTORY
setopt HIST_IGNORE_DUPS
setopt HIST_IGNORE_SPACE
setopt HIST_VERIFY

# plugins
if [ -f /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh ]; then
  source /usr/share/zsh-autosuggestions/zsh-autosuggestions.zsh
fi
if [ -f /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh ]; then
  source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh
fi
if [ -f $HOME/.zsh/zsh-history-substring-search/zsh-history-substring-search.zsh ]; then
  source $HOME/.zsh/zsh-history-substring-search/zsh-history-substring-search.zsh

  bindkey '^[[A' history-substring-search-up
  bindkey '^[[B' history-substring-search-down
fi

# prompt
git_status_symbol() {
  if git rev-parse --is-inside-work-tree &> /dev/null; then
    if ! git diff --quiet &> /dev/null; then
      echo "%F{red}*%f"
    fi
  fi
}
user_host_ssh() {
  if [[ -n "$SSH_CONNECTION" || -n "$SUDO_USER" ]]; then
    echo '%F{red}%n@%m%f:'
  fi
}
autoload -Uz vcs_info
precmd() { vcs_info }
zstyle ':vcs_info:git:*' formats '[%b]'
setopt PROMPT_SUBST
PROMPT='$(user_host_ssh)$(git_status_symbol)%F{green}${vcs_info_msg_0_}%F{cyan}[%~]%f%(#.#.$) '


# git aliases
alias ga='git add'
alias gaa='git add --all'
alias gc='git commit --verbose'
alias gc!='git commit --verbose --amend'
alias gca='git commit --verbose --all'
alias gca!='git commit --verbose --all --amend'
alias glog='git log --oneline --decorate --graph'
function gpa() {
    for server in $(git remote -v | cut -f1 | uniq) ; do
        echo "git push $server"; git push $server
    done
}
function gpat() {
    for server in $(git remote -v | cut -f1 | uniq) ; do
        echo "git push $server --tags"; git push $server --tags
    done
}
alias gst='git status'

# vscode aliases
function vsc {
  if (( $# )); then
    code $@
  else
    code .
  fi
}


# hashes
hash -d www=/var/www

Текст можно просто скопировать и разместить в домашней папке ~/.zshrc. Теперь можно изменить оболочку по умолчанию у нужного пользователя (chsh -s /bin/zsh) и повторно войти в систему. Если вы не установите дополнительные плагины через пакетный менеджер своего дистрибутива или через git clone, то этот функционал просто не будет включен.

Приятного и комфортного вам управления вашими устройствами!


Обратите внимание, что заметки могут обновляться со временем. Это может быть как исправление найденных ошибок, так и доработка содержания с целью более полного раскрытия темы. Информация об изменениях доступна в репозитории на github. Там же вы можете оставить в Issue ваши замечания по данной заметке.


Если данная заметка оказалась вам полезной, можете поблагодарить автора финансово.