Алексей Федорчук
В прошлой статье мы немало времени уделили проблеме выбора командной оболочки. И такой выбор сделали - пока просто поверив мне на слово. В настоящем же разделе я рассчитываю дать ему обоснование.
Как можно догадаться из названия заметки, главным героем ее является командная оболочка Z-Shell или, сокращенно, zsh, о которой на протяжении всего предшествующего повествования не было сказано ни слова. Настало время исправить это упущение.
Итак, zsh - оболочка из клана sh-совместимых, первоначально разрабатывавшаяся Паулем Фальстадом (Paul Falstad), ныне развивается в рамках самостоятельного проекта сообществом энтузиастов (Zsh Development Group) при координации Петера Стефенсона (Peter Stephenson). В отличие от bash, прямого (как, впрочем, и косвенного) отношения к GNU zsh не имеет, распространяется под собственной лицензией BSD-стиля, а, следовательно, является полностью свободной программой.
Существует мнение (и не только мое), что в zsh нашли свое воплощение все прогрессивные тенденции таких развитых оболочек, как bash и tcsh.
И, ознакомившись с его возможностями, с этим трудно не согласиться - в zsh есть все, что было хорошего в тех обеих оболочках, но, если так можно выразиться, в превосходной степени.
Действительно, какими особенностями определяется в первую очередь удобство интерактивной работы в командной оболочке? В порядке, котором с ними сталкивается пользователь, это будут:
Автодополнением клавишей Tab команд, частично введенных в ответ на приглашение оболочки, трудно удивить пользователей bash или tcsh.
Столь же естественно, что при возможности безальтернативного дополнения
именно оно и происходит, а при наличии некоторых альтернатив выводятся
возможные варианты. Однако zsh идет дальше - и после вывода таковых в ответ на последующие нажатия клавиши табулятора начинает автоматический их перебор.
Автодополнение путей к файлам, выступающим в качестве аргументов
команд, - тоже не бог весть какое новшество. И единственное отличие zsh от прочих оболочек выражается здесь в том, что и для путей действует автоматический перебор вариантов клавишей табулятора. А вот то, что автодополнение работает также и для аргументов команд, например, команды man - окажется приятной неожиданностью. Так, чтобы вызвать полное экранное руководство по zsh, достаточно набрать
$ man zsha
и нажать табулятор, чтобы развернуть его до полного
$ man zshall
Более того, автодополнению (и автоматическому перебору его
возможностей) подвержены даже опции многих команд. Это особенно
показательно для таких синтаксически сложных команд, как find. Так, последовательность
$ find / -na
будет автоматически дополнена до
$ find / -name
А после указания этого для указания опции действия можно ограничиться вводом символа дефиса
$ find / -name filename -
и выбрать необходимое действие из предложенного списка, Например, print - вывод на экран, или exec - исполнение сторонней команды.
И еще один частный случай автодополнения уникален для zsh: развертывание путей при сокращенном их наборе. Так, что просмотреть содержимое каталога /usr/portage/distfiles, достаточно набрать в командной строке
$ ls /u/p/di
и нажать клавишу табуляции: сокращенный ввод пути (разумеется, при безальтернативности оного) будет автоматически развернут до полного.
Автодополнение в zsh гармонично сочетается с автокоррекцией (т.н. spelling командной строки). Конечно, и это само по себе не уникально.
Однако проверка правильности ввода и автокоррекция в zsh распространяются не только на встроенные (как в bash) и даже внешние
(как в tcsh) команды, но даже на пути и аргументы. Причем если автокоррекция становится назойливой (например, для команд типа cp или
mv она порывается исправить аргументы на имена существующих файлов), ее можно отключить - и именно только для определенных команд.
Средства навигации по командной строке и ее автоматического
редактирования - необходимое условие комфорта в интерактивной работе
внутри оболочки. Здесь говорить, казалось бы, не о чем - управляющие
клавишные последовательности для таких действий давно уже вошли в
обиход всех командных оболочек, претендующих на развитость. Однако и в
этой области zsh
есть чем похвастаться - в нем задействованы все
комбинации клавиш для перемещения и удаления (как посимвольного, так и
командными "словами" и фрагментами строки), которые существуют в bash и tcsh, причем - построенные по тем же принципам.
Управляющие последовательности в zsh построены по принципу сочетания клавиши Control+литера или Meta+литера, причем вторая комбинация обычно выступает в качестве "усиленного" варианта первой. Так, если Control+D
удаляет символ в позиции курсора, то сочетание Meta+D проделывает это для всех символов от позиции курсора до конца командного слова.
Предусмотрены в zsh и клавишные комбинации для таких
действий, как преобразование регистра литерных символов, "перетасовки"
символов и
командных "слов" в строке, заключения строки в кавычки (а при
необходимости - и экранирования оных символами обратного слэша). Есть,
конечно же, и комбинация для многоуровневой отмены ввода.
Действие большинства простых (двухклавишных) последовательностей дублируется "сложными", вида Meta+литера-Control+литера, которые прекрасно работают и при переключении на кириллическую раскладку клавиатуры.
Легко заметить, что управляющие последовательности в zsh реализованы с стиле emacs.
Однако это - лишь один из возможных режимов, тот, который принят по
умолчанию. При желании ничего не стоит переключить навигацию и
управление в режим vi, если таковой кажется более привычным для пользователя.
Оболочка zsh обладает всеми стандартными средствами
доступа к буферу истории команд - перехода к началу и концу буфера
истории, просмотра оного вперед и назад (как клавишами управления
курсором, так и соответствующими управляющими последовательностями),
обычного и т.н. наращиваемого поиска в обоих направлениях, исполнения
выуженной из буфера команды с автоматическим переходом к следующей.
Плюс - весьма изощренные способы вывода в строку отдельных фрагментов
команд из буфера истории - например, отдельного командного "слова",
начиная с последнего, с дальнейшим перебором "слов" буферизованных
команд назад. Или - вывод полного списка команд из буфера с их
последовательным
перебором в том или ином направлении.
И, наконец, такое мощное средство минимизации пользовательского ввода, как псевдонимы команд (aliases). Разумеется, в zsh (как и в bash или tcsh)
псевдоним может быть присвоен любой команде со сколь угодно длинным
набором опций. Так, куда как проще раз и навсегда определить команду ls как псевдоним самой же себя, но с опциями -FG, нежели каждый раз вспоминать, как отличить в ее выводе каталоги от обычных файлов.
Однако zsh идет дальше: в нем это дополняется возможностью определения псевдонимов для командных конвейеров в форме опции -g
(от
global aliases - именно так именуется эта возможность). Так, всем
известно, что для обеспечения постраничного вывода любой команды
(например, той же ls) вывод этот нужно передать по конвейеру (pipe) программе-pager'у (less или more). Однако не лениво ли - каждый раз вводить что-нибудь вроде
$ ls | less
да еще и не забывать это делать? Если лениво - на помощь придут
глобальные псевдонимы. Опять же раз и навсегда определяем, что опция -g со значением L есть псевдоним для конвейера '| less':
$ alias -g L='| less'
после чего имеем возможность, указывая ее после команды, требующей постраничного ввода, именно его и получать:
$ ls -g L
Казалось бы, не намного проще? Ан нет: ведь ничто не препятствует нам создать еще один, обычный, псевдоним для команды ls с этой опцией, например:
$ alias lp='ls -g L'
чтобы при необходимости постраничного вывода списка каталога именно к нему и прибегнуть.
Важный момент облегчения существования пользователя в любом шелле - вид
приглашения командной строки, должная настройка которого может часто
избавить от лишнего набора команд (уж от команды pwd я по возможности стараюсь избавиться именно таким образом). Так вот, zsh
поддерживает несколько независимо настраиваемых обычных приглашений -
обычное, или первичное, вторичное - для многострочных команд,
"выделенное", приглашение при выводе вариантов автокоррекции и даже
специальное "приглашение" в
правой части командной строки. Которое, конечно, собственно
приглашением не является, но позволяет вывести полезную информацию,
например, текущее время или дату, номер виртуальной консоли, и т.д. Для
пущей экспрессии настраивается также передача символов в любом
приглашении - цветом ли, выделением, инверсией, подчеркиванием.
Очень полезная возможность - различение вида приглашения для
обычного пользователя, получившего права root'а в результате команды su
без опций (то есть без перечитывания профильных файлов администратора),
от собственно пользовательского приглашения - дабы не забывал юзер о
временности своих полномочиях.
Надеюсь, что мне удалось убедить читателя в превосходных интерактивных возможностях оболочки zsh.
Теперь стоит поговорить о функциональности, которая проявляется не
только в интерактивной работе (но и, скажем, при сочинении скриптов).
Функциональность же любой оболочки можно в первом приближении оценить
по количеству встроенных в нее команд. То есть - команд, выполняемых
внутри самого шелла, без порождения новых процессов, как это происходит
при выполнении внешних команд. Очевидно, что такие встроенные команды
будут выполняться быстрее и отъедать меньше ресурсов. Что, конечно, не
скажется при интерактивной работе на современных машинах, но вот в
сложных сценариях - вполне может.
В zsh поддерживается весь набор встроенных команд, стандартизированный для POSIX shell, большинство команд из развитых оболочек bash и tcsh, ну и, разумеется, специфичные для этого шелла команды. Общее число их превышает 80 - примерно столько же, сколько
встроено в tcsh и bash, вместе взятые.
Очень интересна (и удобна) в zsh работа с командными
конструкциями перенаправления. Здесь и множественное перенаправление
вывода, когда результат выполнения команды направляется сразу в
несколько файлов, и множественное перенаправление ввода - когда
команда, напротив, получает аргументы последовательно из более чем
одного файла, перенаправление без команды, когда конструкция типа
$ < filename
просто выведет на экран содержимое указанного файла - без привлечения команд типа cat или less.
При перенаправлении возможна группировка команд по шаблону. Так, файлы с именами вида file1 и file2 можно просмотреть одной командой
$ < file{1,2}
Перенаправление ввода/вывода может иногда заменять конвейеризацию команд. Так, конструкция вида
$ sort < file{1,2}
отсортирует содержимое обоих файлов точно так же, как это сделал бы конвейер команд
$ cat file1 file2 | sort
Наконец, еще одна специфическая особенность zsh -
т.н. пред-исполнимая модификация команд (precommand modifier),
осуществляющаяся перед их интерпретацией. Именно таким образом можно
отменить чрезмерно навязчивую автокоррекцию аргументов для одной
отдельно взятой команды, например, копирования:
$ nocorrect cp file1 file2
Легко видеть, что все изобилие возможностей zsh далеко выходит за рамки стандарта POSIX для командных оболочек. Однако, в подтверждение
своего соответствия оному, zsh, наступая на горло собственной песне, способен к эмуляции POSIX Shell - для этого достаточно создать файл /bin/sh как символическую ссылку на исполнимый файл zsh, например:
$ ln -s /usr/bin/zsh /bin/sh
Впрочем, делать это следует только в случае полной уверенности, что все общесистемные скрипты полностью совместимы с zsh - а такая уверенность может быть только в том случае, если они написаны собственноручно:-). Я успешно применял zsh
в качестве общесистемного шелла в самостройном Linux'е (по мотивам
Linux from Scratch). Однако в других дистрибутивах (и тем более во
FreeBSD) от этого лучше воздержаться.
Кроме того, имеется и некий режим совместимости с командными оболочками csh-клана (в нем я, впрочем, не разбирался).
Здесь перечислена лишь небольшая часть возможностей оболочки zsh,
в частности, я не останавливался на его встроенных функциях, хотя
именно они и есть та база, что обеспечивает все описанное (и не
описанное) богачество возможностей. Не говорил я и о подгружаемых
модулях (по типу plug-ins) - а ведь среди последних есть даже
собственный ftp-клиент. Ибо для этого потребовалось бы пересказать всю
экранную документацию к нему - более дюжины man-страниц общим объемом
(в *.gz-виде) свыше 250 Кбайт, плюс официальное руководство с сайта
проекта, включающее в pdf-формате 260 страниц (к слову - автором этой
документации является тот же Петер Стефенсон).
Тем не менее надеюсь, что я убедил вас в том, что zsh - штука стоящая. Если так - то
Оболочка zsh стандартно входит в большинство (я бы
сказал - во все из мне известных) полнофункциональных дистрибутивов
Linux, в качестве
портов и пакетов доступна во Free- и OpenBSD (на счет NetBSD - просто
не помню), портирована даже в AtheOS (вернее - ее отпрыска Syllable).
Так что проблем с ее получением быть не должно. Устанавливаем zsh штатным для данного дистрибутива методом и переходим к следующему разделу.
Если же почему-либо zsh в составе дистрибутива не обнаружился - отправляемся на ftp://ftp.zsh.org/pub/ или какое-либо его зеркало (список - на hcodep://www.zsh.org),
можно - в каталог distfiles
ftp-серверов FreeBSD или Gentoo. И качаем в свое удовольствие исходники
последней стабильной версии (на данный момент - 4.07), или, при
желании, разрабатываемой (ныне - 4.2) - различий в стабильности между
ними я не заметил.
Исходники zsh распаковываются и собираются обычным образом - ./configure, make, make install,
никаких неожиданностей здесь не предвидится. Единственно, я
предварительно, чисто для интереса, поинтересовался бы опциями
конфигурирования -
$ ./configure --help
Из которых не побрезговал бы опцией --bindir=/bin - это будет полезно, если zsh будет использоваться как login shell.
В дистрибутивах Linux, предусматривающих автоматическое обновление
общесистемных профильных файлов (например, в Gentoo), может возникнуть
необходимость в том, чтобы zsh брал свои переменные окружения из какого-либо общего конфига, например, /etc/profile. Для этого при начальном конфигурировании исходников следует указать
$ ./configure --enable-zprofile=/etc/profile
И еще: ручная сборка zsh может потребоваться в некоторых пакетных дистрибутивах, в которых, будучи установленным из бинарников, он может работать неподобающим образом (о причинах я скажу позже).
Теперь же, установив zsh тем или иным образом, делаем его своей пользовательским шеллом по умолчанию - login shell (одной из команд типа usermod, pw, chsh) и -
Без этого нам, скорее всего, не обойтись. Дело в том, что в свежеустановленном zsh,
мы имеем шанс не увидеть почти ничего из описанных выше прелестей - ни
развертывания сокращений путей, ни автодополнений опций и аргументов,
ни выразительных приглашений командной строки. Перед нами будет
безликая строка с именем машины
(типа localhost%), которая едва-то будет справляться с
обычным автодополнением команд и путей (и то - не обязательно). Могут
возникнуть проблемы даже с вызовом собственной экранной документации man zsh. Почему?
Дело в том, что zsh имеет очень богатый набор
собственных конфигурационных файлов, о которых я скажу чуть ниже. Но
при установке его эти файлы не помещаются автоматически ни в каталог /etc,
ни в
домашний каталог пользователя - то есть ни в одно из тех мест, где их
можно было бы ожидать. Конечно, совсем без первичных настроек zsh не останется: он прекрасно воспринимает их из таких общесистемных профильных файлов, как /etc/profile, /etc/login
и т.д. Однако, во-первых, для этого он должен быть собран должным
образом. А во-вторых, и вести себя при этом он будет почти точно также,
как и соответствующие оболочки (bash или tcsh, а то и /bin/sh - почему, например, во FreeBSD zsh по первости не способен даже к автодополнению команд). От одного из этих профильных файлов zsh унаследует и переменные окружения, включая MANPATH - почему подчас не сможет найти и свою собственную документацию.
Так что для придания zsh полного блеска следует
прибегнуть к его собственным конфигурационным файлам. Правда, сначала
придется отыскать их примеры из штатной поставки - должен заметить, что
в разных системах
они могут обнаружиться в весьма неожиданных местах. Так, во FreeBSD их
штатное место - каталог /usr/local/share/examples/zsh, в дистрибутиве
Gentoo Linux примеры оказываются в /usr/share/doc/zsh-XXX-rX/StartupFiles, в иных - вполне могут оказаться где-нибудь в районе /usr/share/zsh, и т.д.
Для минимизации времени на поиски выдам секрет - примеры конфигурационных файлов zsh, входящие в штатный комплект, называются - zlogin, zshenv и zshrc (возможно, с расширением .gz), и благодаря моей доброте :-) их не трудно будет отыскать командой find - за пределы каталога /usr файлы эти попасть не должны (даже в том случае, если, как в Gentoo, сам исполняемый файл zsh оказывается в каталоге /bin).
После изыска конфигов для сердца вольного есть два пути. Первый -
простой, копируем их в наш домашний каталог в качестве dot-файлов (~/.zlogin, ~/.zshenv и ~/.zshrc,
соответственно), после чего наслаждаемся жизнью. В ряде случаев этого
достаточно, чтобы получить доступ к базовым (но очень даже расширенным,
сравнительно с собратьями)
возможностям этого шелла.
Разумеется, суперпользователь может скопировать эти файлы и в каталог /etc (без точек в имени) - в этом случае они будут определять конфигурацию zsh для всех пользователей, его запускающих (любым образом, о чем - чуть ниже).
Второй путь - попытаться разобраться, за что отвечают скопированные
файлы и как они устроены. Это понадобится в двух случаях - а) для
идеальной настройки своего нового шелла и б) если zsh работает не совсем так хорошо, как вы ожидали (например, не так, как описано выше).
Первый шаг на этом пути - задаться вопросом, а зачем zsh'у так много конфигов, если другие шеллы спокойно обходятся двумя (а то и одним, как
/bin/sh). На это я отвечу, что конфигов в zsh вовсе не много, а очень много: в дополнение к трем примерным в разделе FILES его man-страницы можно найти упоминание еще о zprofile и zlogout (и, соответственно,
~/.zprofile и ~/.zlogout). А в ходе пользования им вы, скорее всего, увидите в своем каталоге еще и такие файлы, как ~/.zcompdump и
~/.zhistory.
Так для чего нам такое богачество? Чтобы разобраться в этом, вспомним о
трех видах функционирования любого шелла - неинтерактивном,
интерактивном и подвиде последнего - главном пользовательском (login shell). Так вот, файл /etc/zshenv (или ~/.zshenv) считывается при каждом запуске любого
экземпляра zsh, независимо от того, происходит он интерактивно или опосредованно. Настройки файла /etc/zshrc (и ~/.zshrc) имеют силу для любого интерактивного запуска zsh. И, наконец, файлы /etc/zlogin и /etc/zprofile оба вместе (как и соответствующая им пара ~/.zlogin и ~/.zprofile) относятся только к тому экземпляру интерактивно запущенного zsh, который выступает в качестве login shell.
Зачем так сложно? А для того, чтобы можно было гибко (и
индивидуально) настроить неинтерактивные, интерактивные и
пользовательские экземпляры шеллов. Действительно, очевидно, что на
настройку неинтерактивного шелла влияет только содержимое файла /etc/zshenv (и ~/.zshenv), на настройку любого интерактивно запущенного экземпляра - уже он же вкупе с /etc/zshrc (и ~/.zshrc), тогда как поведение login shell определяется кумулятивным эффектом всех трех (или даже четырех) их пар.
Хорошо, но зачем же нам два конфига для login shell? - спросите вы меня. Ответ прост -
из соображений совместимости с bash и tcsh. Для пояснения чего вернемся
к истории вопроса. В первозданном шелле Борна существовал только один конфиг - /etc/profile (~/.profile) для любых экземпляров шелла. В bash к нему прибавился еще и файл /etc/bashrc (~/.bashrc) для интерактивного использования (считываемые, естественно, после предыдущего - как более молодой по происхождению).
В csh же набор конфигов был совсем иной. Там изначально существовали два конфига - /etc/csh.env (~/.csh.env) на все случаи жизни и /etc/login (~/.login)
- в качестве конфигуратора login shell, считываемые именно в таком порядке.
В zsh же, дабы удовлетворить привычки пользователей любых
предшествовавших шеллов, были включены оба "логируемых" конфига, причем
порядок их считывания был унаследован
от каждого из родителей. В результате получилась довольно сложная
последовательность при запуске login shell:
zshenv -> zprofile -> zshrc -> zlogin
Причем каждый конфиг сначала, естественно, считывается из каталога /etc,
а затем из домашнего каталога пользователя берется его аналог.
Разумеется, если все четыре файла присутствуют (и там, и там). Что,
сразу скажем, отнюдь не обязательно. Очевидно, что совместное
использование zprofile и zlogin ни малейшего смысла не имеет. Просто бывшим пользователям bash привычней первая схема запуска login shell, бывшим приверженцам tcsh
- вторая. Забегая вперед, замечу, что вообще пользователь может
обойтись только одним конфигом в своем домашнем каталоге (например, ~/.zshrc для любого интерактивного
экземпляра шелла - ведь login shell также будет интерактивным), а все общие настройки получать из общесистемного конфига (например, /etc/profile). Более того, в дистрибутиве Gentoo Linux именно так поступить лучше всего - к причинам вернусь позднее.
Осталось объяснить смысл остальных dot-файлов из пользовательского каталога. Каковой, впрочем, ясен из названий: - это
сценарий, отрабатываем при выходе из ~/.zlogoutlogin shell, ~/.zhistory хранит
историю команд (это и есть ее буфер), а ~/.zcompdump (насколько я понимаю) - делает то же самое, но для встроенных функций zsh. Два последних файла возникают (при выполнении некоторых условий) сами собой, и речи о них больше почти не будет.
Разобравшись с назначением dot-файлов, можно, наконец, выполнить
Процесс этот начнем с того, что отделим зерна от плевел, то есть
решим: а какие же именно файлы нужно настроить. Для начала я оставил бы
в покое все файлы из каталога /etc - вернее, просто не
стал бы копировать туда примеры. Почему? Да потому, что во многих
дистрибутивах
Linux имеются либо предварительно настроенные общесистемные конфиги,
либо предусмотрены средства для их автоматического создания и
обновления (ниже я продемонстрирую это на примере Gentoo). А во FreeBSD
в каталоге /etc принято хранить только профильные файлы общесистемного шелла (сиречь /bin/sh, тогда как настройка пользовательского шелла - сугубо личное дело пользователя.
Так что ограничиваемся только нашим домашним каталогом. Однако и здесь
многое зависит от ОС и дистрибутива. Так, в user-ориентированных
дистрибутивах Linux первый кандидат на удаление (или не-копирование) -
файл ~/.zshenv.
Конечно, его можно создать в предельно облегченном виде (например,
настройка приемов неинтерактивной работы здесь абсолютно лишняя).
Характерно, что в штатном примере zshenv присутствует только переменная PATH. Но ведь ее можно получить из общесистемного /etc/profile, не так ли?
Особенно лишним выглядит ~/.zshenv для пользователей Gentoo. Как можно прочитать в соответствующей документации,
здесь существует прекрасный механизм установки общесистемных переменных - env-update, автоматически обновляющий главный профильный конфиг
/etc/profile. Так что при отсутствии ~/.zshenv все необходимое (в актуальном виде) будет браться оттуда. В присутствии же его - zsh откажется от считывания /etc/profile, в результате чего такие переменные, как локаль, EDITOR, PAGER и т.д., придется определять дополнительно.
А вот во FreeBSD и тех дистрибутивах Linux, которые (как, например,
CRUX и Archlinux) не имеют средств автоматизации установки
общесистемных переменных, файл ~/.zshenv
оказывается практически необходимым. И ниже я попытаюсь обосновать это
(а заодно и продемонстрировать, что в нем может содержаться).
Далее, решаем, а нужен ли нам отдельный конфиг для login shell, и если нужен - то какой из них - ~/.zlogin или ~/.zprofile.
Теоретически, можно обойтись и без того, и без другого. Уж без
последнего - так наверняка, не зря же его нет ни среди штатных
примеров, ни среди многочисленных конфигов, которыми поделились с
народом активные пользователи zsh (см. источники информации). А файл ~/.zlogin у меня, например, есть, хотя содержание его может варьировать от системы к системе.
Не помешает и файл ~/.zlogout - его возможное содержание также будет рассмотрено позднее.
Остается главный (по крайней мере, самый большой) конфиг - ~/.zshrc. Что с ним делать? Это легко уяснить, ознакомившись со штатным примером.
А в примере этом мы найдем и настройку приглашений командной строки, и
определение псевдонимов - обычных и глобальных, для конвейеров команд,
и определение переменной cdpath, и величину буфера истории
команд, и многое, многое другое.
Описывать все опции из примера было бы долго и скучно. А потому здесь я остановлюсь только на тех, которые создают удобство при интерактивной работе и определяют внешний вид нашего шелла.
Начнем с последнего, то есть приглашения командной строки. В этом качестве могут использоваться:
Плюс к этому приглашения могут быть оформлены визуально различно: выделением жирным шрифтом (boldface mode) или повышенной яркостью (underline mode), инвертированием текста/фона (standout mode), а также цветами. Все это позволяет добиться максимальной информативности приглашения и его внешней выразительности.
Из опций настройки интерактивности - вспомним о том, что обеспечивает расширенные возможности автодополнения. А обеспечиваются они строками, следующими после комментария:
# Setup new style completion system. To see examples of the old style (compctl
# based) programmable completion, check Misc/compctl-examples in the zsh
# distribution.
и имеющими вид
autoload -U compinit compinit
Вообще, штатный пример файла zshrc очень неплохо прокомментирован - по аглицки, но разобраться можно:-).
Далее - опции управления историей команд. Здесь для начала следует
определить файл, в котором эта история будет храниться (в примере по
умолчанию таковой отсутствует, и никакая история по умолчанию не
сохраняется по выходе из сеанса zsh):
HISTFILE=~/.zhistory
Теперь - объем нашей исторической памяти, задаваемый двумя опциями -
HISTSIZE=1000
определяющей память текущего сеанса, и
SAVEHIST=1000
устанавливающая размер буфера, сохраняемого в HISTFILE.
Очевидно, что первую бессмысленно делать больше второй (рекомендуются
равные значения - в моем примере они взяты просто с потолка).
Для обеспечивая наращивания файла истории после каждого сеанса работы (в пределах установленной квоты) определяем:
setopt APPEND_HISTORY
Объем исторической памяти можно установить любым, однако не стоит расходовать его на повторение дублирующихся команд, ошибочными нажатиями Enter в пустой строке и т.д.:
setopt HIST_IGNORE_ALL_DUPS setopt HIST_IGNORE_SPACE setopt HIST_REDUCE_BLANKS
Можно настроить и еще много чего, но на этом пока закончим (некоторые
опции будут подробнее рассмотрены в следующем разделе). Потому что
наступило время подумать, а что представляют из себя установленные нами
опции (типа compinit и т.д.)? А представляют они собой по преимуществу встроенные функции из комплекта zsh, которые в изобилии можно
обнаружить в каталоге /usr/share/zsh/4.0.6/functions. И именно
благодаря этим функциям и удается все настроить.
А еще - становится понятным, почему иногда zsh в
пакетных дистрибутивах, устанавливаясь из бинарников, ведет себя не
вполне
подобающим образом. Да именно потому, что комплект функций в поставке и
их использование в файлах примеров не вполне идентичны (по крайней
мере, мне такие случаи встречались). При сборке же из исходников с
сайта проекта, насколько мне довелось наблюдать, идентичность эта
гарантирована. Тот же результат, естественно, достигается и в Source
Based дистрибутивах (или, скажем, в системе портов FreeBSD).
Вообще говоря, я долго не мог понять, почему пользователи не любят zsh.
И в конце концов пришел к выводу - именно потому, что сборщики
дистрибутивов подчас не умеют его готовить. Это - не только мое мнение:
многие пользователи пакетных дистрибутивов свидетельствуют, что
установленный из "коробки" zsh в их системах работает весьма криво. Так что позволю себе привести
Как-то на одном форуме промелькнул вопрос - а как сделать, чтобы в командной строке zsh клавиши типа Delete, End, Home
вели себя нормально (по умолчанию они этого делать не собираются). У
меня до этого долго не доходили руки - я в этих целях привык к
клавишным комбинациям (см. следующий раздел). Однако некоторое чувство
дискомфорта преследовало: как же так, какой-то там bash умеет нормально обращаться с клавишами, а любимый zsh
- не умеет. А тут и случай представился: во время затеянной в рамках
мегатестирования тотальной пересборки Qt/KDE/иже_с_ними времени
образовалось - вагон и маленькая тележка. И я наконец-то разобрался с
клавишами в zsh. О чем с удовольствием рапортую в этом разделе.
Из многочисленных zsh-конфигов я (в настоящее время) использую: ~/.zshenv, считываемый при каждом запуске экземпляра оболочки (интерактивном и неинтерактивном), ~/.zshrc, считываемый при каждом интерактивном ее запуске, и ~/.zlogin, считываемый при каждом запуске zsh в качестве login shell. При этом установка переменных окружения происходит в стиле C-Shell: сначала из ~/.zshenv, затем из ~/.zshrc и, наконец, из ~/.zlogin.
Так что первым в моей схеме идёт ~/.zshenv. Он
оказывает воздействие только при shell-скриптинге. Поэтому у меня он
очень мал. В Linux-варианте (для Archlinux) он выглядит так:
# # My ~/.zshenv for Linux #export PATH="/bin:/usr/bin:\ /usr/local/bin:/usr/X11R6/bin:\ /opt/bin:/opt/kde/bin"
Во FreeBSD все примерно то же, но, естественно, /opt в PATH включать не требуется:
# # My ~/. zshenv for FreeBSD # PATH="/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin" QTDIR=/usr/local/qt PATH="$PATH:$QTDIR" MANPATH="$QTDIR/doc/man:$MANPATH" LD_LIBRARY_PATH="$QTDIR/lib:$LD_LIBRARY_PATH" export QTDIR PATH MANPATH LD_LIBRARY_PATH
Понятно, что оба файла можно записать поизящней (с точки зрения формы), но - так уж они исторически склались.
Основное внимание я уделяю файлу ~/.zshrc, так как им определяется поведение zsh не только при авторизации в консоли, но и при запуске терминальных окон в Иксах.
# # My ~/.zshrc # # Path для поиска командой cd: то есть вместо cd $HOME/docs/editors/ # можно набирать просто cd editors cdpath=(~/media ~/docs) ## Установка нормального поведения клавиш Delete, Home, End и т.д.: case $TERM in linux) bindkey "^[[2~" yank bindkey "^[[3~" delete-char bindkey "^[[5~" up-line-or-history bindkey "^[[6~" down-line-or-history bindkey "^[[1~" beginning-of-line bindkey "^[[4~" end-of-line bindkey "^[e" expand-cmd-path ## C-e for expanding path of typed command bindkey "^[[A" up-line-or-search ## up arrow for back-history-search bindkey "^[[B" down-line-or-search ## down arrow for fwd-history-search bindkey " " magic-space ## do history expansion on space ;; *xterm*|rxvt|(dt|k|E)term) bindkey "^[[2~" yank bindkey "^[[3~" delete-char bindkey "^[[5~" up-line-or-history bindkey "^[[6~" down-line-or-history bindkey "^[[7~" beginning-of-line bindkey "^[[8~" end-of-line bindkey "^[e" expand-cmd-path ## C-e for expanding path of typed command bindkey "^[[A" up-line-or-search ## up arrow for back-history-search bindkey "^[[B" down-line-or-search ## down arrow for fwd-history-search bindkey " " magic-space ## do history expansion on space ;; esac # Примечание: если, скажем, в KDE для konsole # выбрать тип Linux console, необходимости # во второй секции нет. Консоль Linux в KDE можно
спокойно установить и во FreeBSD. # К слову сказать, совсем уж нормального # поведения клавиш в syscons я до сих пор # не добился:-( # Use hard limits, except for a smaller stack and no core dumps unlimit limit stack 8192 limit core 0 limit -s # Установка атрибутов доступа для # вновь создаваемых файлов umask 022 # Исправление поведения less - для ликвидации # лишних Esc и прочего безобразия при # выводе man-страниц. # Насколько мне известно, нужно только в некоторых # дистрибутивах Linux export LESS="-R" # Установка alias'ов ## alias'ы для команд, не требующих коррекции, но требующих подтверждения alias mv='nocorrect mv -i' # переименование-перемещение c пogтвepжgeнueм alias cp='nocorrect cp -iR' # рекурсивное копирование с подтверждением alias rm='nocorrect rm -i' # удаление с подтверждением alias rmf='nocorrect rm -f' # принудительное удаление alias rmrf='nocorrect rm -fR' # принудительное рекурсивное удаление alias mkdir='nocorrect mkdir' # создание каталогов без коррекции ## Примечание: если не определить здесь nocorrect, ## zsh будет настойчиво предлагать подстановку ## существующих имен при создании каталогов, ## копировании и т.д. ## Разные полезные (ИМХО) alias'ы alias h=history alias grep=egrep ### вывод свободного дискового пространства ### в мегабайтах alias df='df -m' ### Представление вывода less в more-подобном виде ### (с именем файла и процентом вывода) alias less='less -M' ### Русский словарь для ispell по умолчанию alias ispell='ispell -d russian' ## aliases для команды ls< ### показ классификации файлов в цвете и ### символически (Linux) alias ls='ls -F --color=auto' ### Во FreeBSD достаточно alias ls='ls -F' ### вывog в gлuннoм фopмaтe alias ll='ls -l' ### вывog всех файлов, включая dot-фaйлы, кромe . u .. alias la='ls -A' ### вывog вcex фaйлoв в gлuннoм фopмaтe, вkлючaя inodes alias li='ls -ial' ### вывод только каталогов alias lsd='ls -ld *(-/DN)' ### вывog тoльko dot-фaйлoв alias lsa='ls -ld .*' # Установка глобальных псевдонимов # для командных конвейеров alias -g M='|more' alias -g L='|less' alias -g H='|head' alias -g T='|tail' alias -g N='2>/dev/null' # Ниже даны опции, относящиеся к функциям zsh, # которыми собственно и определяется мощь этой оболочки # Shell functions setenv() { typeset -x "${1}${1:+=}${(@)argv[2,$#]}" } # csh compatibility freload() { while (( $# )); do; unfunction $1; autoload -U $1; shift; done } # Where to look for autoloaded function definitions fpath=($fpath ~/.zfunc) # Autoload all shell functions from all directories in $fpath (following # symlinks) that have the executable bit on (the executable bit is not # necessary, but gives you an easy way to stop the autoloading of a # particular shell function). $fpath should not be empty for this to work. for func in $^fpath/*(N-.x:t); autoload $func # automatically remove duplicates from these arrays typeset -U path cdpath fpath manpath # Указание путей к man-страницам. ## Linux: manpath="/usr/man:/usr/share/man:\
/usr/local/man:/usr/X11R6/man:/opt/qt/doc" ## FreeBSD: manpath="/usr/share/man:/usr/local/man:/usr/X11R6/man" export MANPATH # Список хостов, к которым будет применяться # автодополнение при наборе в командной строке # например, как аргументов браузера или # ftp-клиента (see later zstyle) hosts=('hostname' ftp.freebsd.org ftp.archlinux.org) # Установка вида приглашения ## Обычное приглашение вида ~%=> ## (каталог от домашнего - пользователь/root - стрелка) PROMPT='%~%#=> ' ## Приглашения для второй линии многострочных команд ## вида #_строки>
PROMPT2='%i%U> ' ## Приглашение с правой стороны экрана вида ## 19:15 vc/5 (время - номер консоли) RPROMPT=' %T %y%b' # Всякие переменные ## файл истории команд ## если не указан, история не будет сохраняться ## при выходе из сеанса HISTFILE=~/.zhistory ## Число команд, сохраняемых в HISTFILE SAVEHIST=5000 ## Чucлo кoмaнд, coxpaняeмыx в сеансе HISTSIZE=5000 ## Примечание: ## рекомендуются равные значения для ## SAVEHIST и HISTSIZE DIRSTACKSIZE=20 # Опции истории команд ## Дополнение файла истрии setopt APPEND_HISTORY ## Игнopupoвaть вce пoвтopeнuя команд setopt HIST_IGNORE_ALL_DUPS ## Игнopupoвaть лишние пpoбeлы setopt HIST_IGNORE_SPACE ## Удалять из файл истории пустые строки setopt HIST_REDUCE_BLANKS # Установка-снятие опций шелла setopt notify globdots correct pushdtohome cdablevars autolist setopt correctall autocd recexact longlistjobs setopt autoresume histignoredups pushdsilent noclobber setopt autopushd pushdminus extendedglob rcquotes mailwarning unsetopt bgnice autoparamslash ## Отключение звукового сигнала ## при ошибках
setopt No_Beep ## Нe cчuтaть Control+C зa выxog uз oбoлoчku setopt IGNORE_EOF # Autoload zsh modules when they are referenced zmodload -a zsh/stat stat zmodload -a zsh/zpty zpty zmodload -a zsh/zprof zprof zmodload -ap zsh/mapfile mapfile # Опции общего поведения # bindkey -v # режим навигации в стиле vi bindkey -e # peжuм нaвuгaцuu в cтuлe emacs bindkey ' ' magic-space # also do history expansion on space bindkey '^I' complete-word # complete on tab, leave expansion to _expand # Для разворота сокращенного ввода типа cd d/e в docs/editors autoload -U compinit compinit # Completion Styles # list of completers to use zstyle ':completion:*::::' completer _expand _complete _ignored _approximate # allow one error for every three characters typed in approximate completer zstyle -e ':completion:*:approximate:*' max-errors \ 'reply=( $(( ($#PREFIX+$#SUFFIX)/3 )) numeric )' # insert all expansions for expand completer zstyle ':completion:*:expand:*' tag-order all-expansions # formacodeing and messages zstyle ':completion:*' verbose yes zstyle ':completion:*:descriptions' format '%B%d%b' zstyle ':completion:*:messages' format '%d' zstyle ':completion:*:warnings' format 'No matches for: %d' zstyle ':completion:*:corrections' format '%B%d (errors: %e)%b' zstyle ':completion:*' group-name '' # match uppercase from lowercase zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}' # offer indexes before parameters in subscripts zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters # command for process lists, the local web server details and host completion #zstyle ':completion:*:processes' command 'ps -o pid,s,nice,stime,args' #zstyle ':completion:*:urls' local 'www' '/var/www/htdocs' 'public_html' zstyle '*' hosts $hosts # Filename suffixes to ignore during completion (except after rm command) zstyle ':completion:*:*:(^rm):*:*files' ignored-pacodeerns '*?.o' '*?.c~' \ '*?.old' '*?.pro' # the same for old style completion #fignore=(.o .c~ .old .pro) # ignore completion functions (until the _ignored completer) zstyle ':completion:*:functions' ignored-pacodeerns '_*' # Флаги оптимизации для gcc CFLAGS="-O3 -march=pentium4 -fomit-frame-pointer \ -funroll-loops -pipe -mfpmath=sse -mmmx -msse2 -fPIC" CXXFLAGS="$CFLAGS" BOOTSTRAPCFLAGS="$CFLAGS" export CFLAGS CXXFLAGS BOOTSTRAPCFLAGS
И, наконец, файл ~/.zlogin. Что осталось неохваченным в ~/.zshrc и требуется только при авторизации в системе? Правильно, пользовательские переменные для определения терминала, редактора, пейджера и т.д.
#
# My ~/.zlogin for FreeBSD
#
from 2>/dev/null
EDITOR=joe
PAGER=less
TERM=${TERM:-cons25r}
export EDITOR PAGER TERM
Кроме того, в Linux'е здесь же резонно установить locale (во FreeBSD локаль лучше определять через класс пользователя). И потому для Linux - дополнение:
# # My ~/.zlogin appendix for Linux # # Установка всех локально-зависимых переменных, # кроме LC_ALL
export LANG="ru_RU.koi8r" # Установка десятичной точки # вместо запятой # (требуется для некоторых счетных программ) export LC_NUMERIC="POSIX"
Да, самое последнее - файл ~/.zlogout, отрабатываемый по завершении сеанса пользовательского шелла. У меня от включает две строчки
sync clear
назначение которых более чем понятно (синхронизация дисковых кэшей и очистка экрана).
Это мои пользовательские конфиги. Почти те же самые я использую и
для root'а, с минимальными коррективами. Так, переменная path в /root/.zshenv дополняется значениями
/sbin:/usr/sbin:/usr/local/sbin
В /root/.zshrc опция cdpath имеет вид
cdpath=(/etc /usr)
Кроме того, во FreeBSD здесь я исключаю переменные CFLAGS, CXXFLAGS и BOOTSTRAPCFLAGS, так как от лица root'а все собирается через систему портов, где их аналоги определяются в файле /etc/make.conf.
А в /root/.zlogin локаль (в Linux) установлена как
export LANG="POSIX"
так как некоторые программы упорно не желают собираться при какой-либо иной.
Я уже упоминал, что проект zsh прекрасно документирован, один User's Guide чего стоит. Однако внимание прессы,
как онлайновой, так и бумажной, к нему явно недостаточно. И потому из
дополнительных источников информации на ум приходит только (если не
считать отрывочных упоминаний в паре книжек по Linux) статья Мэтта
Чапмена (Macode Chapman)
- Curtains up: introducing the Z shell. Да еще недавно появился на русском языке материал Алексея Отта Командный процессор Zsh
Много полезного можно узнать из анализа dot-файлов различных пользователей, ссылки на которые отыскиваются как на www.zsh.org, так и в иных местах. Собирать все эти ссылки мне было лениво (да подчас я и не помню, где их отыскал), так что все свои находки я собрал воедино в виде отдельного тарбалла.