За что я люблю Zsh. Конфигурационные файлы

Алексей Федорчук

Да, дорогие мои читатели, больше всего я люблю Zsh за возможности конфигурирования, ограниченные только потребностями и пониманием своих действий. И в знак своей любви на этой странице размещаю свой ~/.zshrc, прокомментированный, по мере сил, подробно. Этот конфиг существует с 2001 года, кочуя с машина на машину, из системы в систему, постоянно модернизируюсь в соответствие с изменениями моих потребностей и возможностей Zsh. И в текущем состоянии он обеспечивает все функции и особенности, о которых я говорил ранее, и некоторые другие, о которых я, возможно, ещё напишу.

Данный конфиг может быть использован полностью или фрагментарно всеми заинтересованными лицами: блоки, заключённые в теги <pre></pre>, пригодны для прямого копирования, за одним исключением, о которому будет сказано в своё время. Однако я отнюдь не призываю к этому, напротив: настоятельно рекомендую, используя данный конфиг и аналогичные, которые можно найти в сети, по мере сил и возможности создавать конфиг собственный. Ибо хороший (для конкретного применителя) ~/.zshrc — это не результат, а процесс, и причём процесс преувлекательный.

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

  • это ~/.zshrc — то есть «домашний» конфигурационный файл для командной оболочки Zsh;
  • используется только в интерактивных её экземплярах;
  • содержит крманды для определения псевдонимов, функций, опций и прочих кейбиндингов;
  • укладывается в последовательность считывания конфигов таким образом: zshenv, zprofile, zshrc, zlogin.

Всё это потибрено унаследовано от прототипа, распространяющегося разрабочиками Zsh. От себя я добавил лишь такую строку:

#
# Alv's edition for Mint, Ubuntu and Kinsmen
#

Это не значит, что данный конфиг нельзя использовать вне семейства Ubuntu: подавляющая часть его строк будет иметь силу в любых дистрибутивах Linux’а или в BSD-системах. Но отдельные его блоки (специально оговоренные) в них просто не будут иметь смысла.

Далее начинается собственно строки определения конфигурируемых параметров. Для удобства восприятия (по крайней мере, моего собственного) они разделены на блоки «целевого назначения». Последовательность блоков, как и строк внутри них, в большинстве случаев рояля не играет, отдельные исключения также оговорены специально.

Поскольку всё имеет своё начало, начать свой конфиг мне показалось логичным с блока строк, имеющих отношение к истории команд. Перво-наперво — определение числа команд, сохраняемых в буфере во время данного сеанса, имени файла истории, и числа сохраняемых в нём команд:

HISTSIZE=2000
HISTFILE=~/.zhistfile
SAVEHIST=10000

Обычно для HISTSIZE и SAVEHIST рекомендуют принимать одиниковые значения (по умолчанию при автоматическом конфигурировании они равны 1000). Однако если действительно трудно представить ввод более чем тысячи команд в течении сеанса, то вот за весь цикл жизнедеятельности оболочки в системе превысить этот лимит достаточно просто.

Кроме того, надо учесть, что в обоих случаях сохраняются не просто команды, а целые директивы с опциями и аргументами, перенаправлениями и конвейерами, подчас достаточно сложными и редко используемыми. В Zsh имеются очень эффективные механизмы извлечения командных строк из сохранённой истории — не только по именам команд, но и по их опциям и аргументам. Обычно этим мало кто заморчивается, однако в некоторых, пусть и не частых, случаях такие командные конструкции могут потребоваться вторично. И тогда приятно сознавать, что они храняться в файле истории, откуда вытащить их всё равно проще, чем пытаться воспроизвести по памяти или отыскивать аналоги в сети.

Так что со временем я, увеличив на всякий пожарный случай HISTSIZE вдвое, отвёл под SAVEHIST 10000 строк. Кстати, когда предупреждают о том, что увеличение обоих значений может привести к торможению, следует учитывать, что в памяти постоянно находится только содержимое HISTSIZE, тогда как из SAVEHIST оно извлекается по мере необходимости. Не говоря уже о том, что при современных машинах об этом просто смешно говорить.

Имя файла истории я тоже изменяю на ~/.zhistfile. Во-первых потому, что иногда по старой памяти балуюсь Tsch, а в ней файл истории по умолчанию также именуется ~/.histfile (собственно, оттуда он в Zsh и был потибрен, в хорошем смысле этого слова). А во-вторых, просто для удобства восприятия — чтобы все имеющие отношение к Zsh файлы в домашнем каталоге были рядом.

Однако продолжим наши исторические опции. Следующие строки задают условия сохранения команд в файле истории:

setopt  INC_APPEND_HISTORY
setopt  HIST_IGNORE_ALL_DUPS
setopt  HIST_REDUCE_BLANKS
setopt  HIST_IGNORE_SPACE

Они определяют, соответственно:

  1. инкрементное наращивание файла истории — без указания этой опции (или одной из однотипных) его прежние команды будут заменены командами текущего сеанса;
  2. удаление предыдущих полных дубликатов нововведённых командных конструкций;
  3. избавление от пустых строк, возникающих после ошибочного нажатия Enter в «голом» приглашении;
  4. удаление лишних пробелов из командной конструкции.

Зачем нужны пукнты 2-4 — ясно без комментариев. А вот о пункте 1-м надо сказать несколько слов. Ибо он не просто обеспечивает наращивание файла истории (для этого было бы достаточно опции, APPEND_HISTORY), но делает это в ходе сеанса, не дожидаясь его завершения. В результате команда, введённая в одном терминальном окне или вкладе терминала, будет доступна в истории команд другого терминала или вкладки (хотя и с некоторой задежкой).

Далее следуют две очень важные строки, определяющие одну из полезнейших возможностей Zsh — так называемый history-substring-search (о нём говорилось на предшествующих страниц):

bindkey "^[[A" up-line-or-search
bindkey "^[[B" down-line-or-search

Следующие две строки касаются уже простого пролистывания истории в командной строке, позволяя делать это клавишами PageUp и PageDown (а не только стрелками Up и Down, которые в этом качестве работают всегда и везде):

bindkey "^[[5~" up-line-or-history
bindkey "^[[6~" down-line-or-history

Этими строками перебрасывается логический мостик к определению кейбиндингов для клавиш, которые в Zsh по умолчанию работают «неправильно» в большинстве терминалов (если не во всех). У меня это Home, End, Delete — их поведение исправляется такими, соответственно, строками:

bindkey "^[OH" beginning-of-line
bindkey "^[OF" end-of-line
bindkey "^[[3~" delete-char

Это как раз пример тех строк, которые as is копировать не нужно. Во-первых, в общем случае, могут не работать другие клавиши (скорее, не только эти). Во-вторых же и главных, в более иных терминалах коды тех же клавиш могут быть совсем другими. Какими — легко определить, нажав Control+V, а затем «неправильную» клавишу. Именно таким образом получены коды для Home, Endи Delete в системе, в которой сочиняются эти строки.

Теперь — опции, определяющие магию Zsh при навигации по файловой системе:

cdpath=(/home/current /home/current/alv.me /etc)
setopt autocd

Первая строка позволяет с помощью команды cd переходить в подкаталоги перечисленных каталогов, не набирая никаких путей, ни относительных, ни абсолютных, вторая же — обходиться без команды cd (разговор о чём уже был).

На грани между опциями навигации и автодополнения находятся такие строки:

setopt menucomplete
zstyle ':completion:*' menu select=1 _complete _ignored _approximate

Они в паре обеспечивают «менюобразный» вывод списка доступных дополнений по нажатию клавиши табуляции (о чем также говорилось ранее). И это как раз тот случай, когда последовательность строк имеет значение.

Аналогично и со следующими строками — теми самыми, которые обеспечивают волшебство развёртывания сокращённого ввода пути в полный:

autoload -Uz compinit
compinit

Расширенные подстановки и дополнения обеспечиваются вот этими строками:

setopt extendedglob nomatch notify
zstyle ':completion:*' completer _expand _complete _ignored _correct _approximate

Строка

zstyle ':completion:*' use-compctl false

знаменует собой отречение от старого мира — системы дополнения compctl, в пользу новой системы compsys.

Строка

zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'

устанавливает равноправие при дополнениях символов нижнего регистра с верхним.

А строка

zstyle :compinstall filename '/home/zsh/.zshrc'

фиксирует файл, в который compinstall (функция автоматического конфигурирования compsys) будет вносить свои изменения при грядущих её вызовах (если они, конечно, будут).

Пора переходить к псевдонимам. Сначала — серия таковых для команд манипуляции файлами, предписывающие запрос подтверждения на таковые или, напротив, форсированное исполнение, в зависимости от ситуации:

alias mv='mv -i'
alias cp='cp -iR'
alias cpr='cp -fR'
alias rm='rm -i'
alias rmf='rm -f'
alias rmrf='rm -fR'

Оказывается, что для одной-единственной команды ls можно придумать больше псевдонимов, чем для всех файломанипулирующих команд, вместе взятых:

alias ls='ls -F'
alias ll='ls -lh'
alias la='ls -A'
alias li='ls -ial'
alias lsd='ls -ld *(-/DN)'
alias lsa='ls -ld .*'

На самом деле их можно придумывать ещё и ещё — этот тот необходимый минимум, который я в состоянии запомнить без вреда для рассудка. Расшифровывать псевдонимы не буду — кому надо, и так могут сорвать с них маски, а кто не знает — так ему это и не нужно.

Далее идёт серия псевдонимов для различных команд и утилит разного назначения. Здесь также расшифровка будет лишней. Ибо они или оболее-менее общеприняты:

alias h=history
alias df='df -h'
alias du='du -h'

Либо обусловлены давними привычками (как, например, more-образный вывод команды less):

alias less='less -M'
alias wget='wget -c'
alias nano='nano -$'

Либо связаны со спецификой деятельности:

alias wcl='wc -l'
alias wcw='wc -w'
alias wcm='wc -m'
alias wcc='wc -c'

Так что можно переходить к следующей убойной фиче Zsh — определению глобальных псевдонимов:

alias -g N='2>/dev/null'
alias -g L='|less'
alias -g G='|grep'
alias -g W='|wc -m'

Где, впрочем, комментарии тоже излишни.

А посему перехожу к тем самым дистрибутив-специфическим блокам, которые я предназначил для применения в Mint’е. Это — псевдонимы для субкоманд утилиты apt, призванные минимизировать ввод при наиболее частых действиях по пакетному менеджменту:

alias aptin='sudo apt install --yes'
alias apter='sudo apt purge'
alias aptup='sudo apt update'
alias aptug='sudo apt upgrade'
alias aptse='apt search'
alias aptsh='apt show'

Правда, хотя эти псевдонимы и имеют смысл только в deb based дистрибутивах, в них нет ничего Zsh-специфичного. В отличие от альтернативного метода, основанного на псевдонимах глобальных, которые определяются для соответствующих аргументов команды sudo.

alias -g Au='apt update'
alias -g Ag='apt upgrade'
alias -g Ai='apt install --yes'
alias -g Ap='apt purge'
alias -g Ar='add-apt-repository'

Впрочем, эти методы друг другу не мешают, а даже очень наоборот, могут дополнять друг друга — но это будет темой отдельной заметки.

Хотя я и утверждал не так давно, что приглашение оболочки — нечто вроде вешалки для театра, сам добрался до этой темы только к концу своего конфига. Однако вот — обычное левосторонне приглашение:

#PROMPT='%B[%n]$=>%b '

Вторичное приглашение:

#PROMPT2='%i%U> '

Правостороннее приглашение:

#RPROMPT=' %B[%~]%b '

А вот это — альтернативы, которыми я баловался во время сочинения прошлой заметки. Все они начинаются с такой пары строк:

autoload -Uz promptinit
promptinit

После которых вызывается уже одна из конкретных тем:

#prompt fade
prompt fade white grey blue
#prompt clint

Естественно, что остальные строки должны быть закомментированы.

Осталось немного — всякая всячина. Например, предотвращение выхода из оболочки после случайного нажатия Control+C в пустой командной строке:

setopt IGNORE_EOF

Отключение раздражающего звукового сигнала при ошибках набора:

setopt NO_BEEP

Фиксация emacs-образного поведения клавиш (хотя это и так имеет место быть по умолчанию):

bindkey -e

И под занавес — определение пары переменных среды, для начала умолчального пейджера. Хотя я и писал недавно, что расширенное перенаправление делает его практически не нужном, но, кроме всего прочего, это ещё и средство для просомтра man-страниц:

export PAGER="less"

И умолчальный редактор: не смотря на свою любовь к Joe, навыки работы с ним я утратил напрочь, поэтому так:

export EDITOR="nano"

Вот вроде и всё. Остаётся последний дистрибутив-специфичный стришок — исправление нехорошего поведения в Ubuntu и всех её сородичах history-substring-search. А точнее, никакого — эта фича без дополнительных мер просто не работает.

В принципе, это тоже можно было бы сделать в рамках ~/.zshrc, но исторически сложилось так, что я пошёл другим путём — созданием файла ~/.zshenv с единственной строкой:

DEBIAN_PREVENT_KEYBOARD_CHANGES=yes

Вот тепень действительно всё — с конфигурированием Zsh «мануальным» способом покончено. А к способу «инструментальному», то есть автоконфигурированию, я вернусь после некоторого тайм-аута.

Содержание