Книга о Cintu. Часть 2. Применение системы. Глава 8. Командная оболочка Zsh
Ближайшие страницы посвящены приёмам работы в командной строке вообще и в Zsh в особенности. Именно эта командная оболочка принята в Cintu как регистрационная (login shell) по умолчанию, вместо традиционного для почти всех дистрибутивов Linux оболочки Bash.
Zsh и мифы о нём
Вокруг командной оболочки Zsh линуксоидами создано не намного меньше легенд и мифов, чем древнеримскими греками — вокруг Троянской войны. Ибо, как «Илиада» описывает один из эпизодов осады города Илиона (который настолько не совсем Троя, что совсем даже не она), так и от прочтения всяких сетевых материалов про Zsh складывается впечатление, что говорят о какой-то другой оболочке, вовсе не той, с которой её применители имеют дело изо дня в день.
Легенды и мифы эти столь стары, что давно превратились в предрассудки. Например, многие годы по сети кочует фраза, до первоисточника которой нам так и не удалось докопаться:
По возможностям zsh больше всего похож на ksh.
Это — первый миф. Потому что Zsh похож на Ksh не более, чем Bash, Ash или Dash. А, как все перечисленные оболочки, с ним совместим. И было бы странно, если бы было иначе: всё это — оболочки, совместимые со стандартом POSIX Shell, при разработке которого за основу был принят именно шелл Корна.
И, раз уж зашла речь о совместимости. Бытует мнение, что Zsh отягощён совместимостью с Csh и Tcsh. Однако тягот тут нет ни малейших. Просто те, кто привык к синтаксису C Shell’а, могут использовать его в своих пользовательских конфигах и скриптах. А могут — не использовать, никто не неволит их это делать. Подобно тому, как обладательниц прокладок (не помню каких) вовсе не заставляют прыгать с парашютом или нырять с аквалангом.
Второй миф как раз и касается скриптов: якобы Zsh не подходит для их сочинения. С этим трудно спорить: для этого он подходит не больше, чем ГТТ — для гонки по автострадам. Потому что как тот, так и другой создавались для совершенно других целей: ГТТ — для езды в отсутствии дорог вообще, Zsh — для интерактивной работы в CLI.
И тут надо заметить, что Bash изначально тоже задумывался как оболочка для интерактивной работы. Хотя в «чистом» виде её возможности в этом плане достаточно ограничены: значительная часть интерактивного функционала Bash’а обеспечивается дополнительными пакетами readline
и bash-comletion
. Правда, в большинстве дистрибутивов они устанавливаются вместе с Bash автоматически. Однако, во-первых, это бывает не всегда. А во-вторых, даже с ними в интерактивном режиме возможности Bash по сравнению с Zsh выглядят как джип супротив ГТТ — в тундре.
Что же до скриптинга — для Zsh сочинять скрипты, конечно, не возбраняется, однако — для работы в пределах одной отдельно взятой машины (или серии машин в руках одного хозяина). В произвольной машине Zsh может или отсутствовать как класс, или быть настроен несколько иначе, и последнее иногда отражается на выполнении сценариев оболочки (вплоть до их невыполнения). С чем связан ещё один миф — что Bash для целей скриптинга подходит больше.
Но тут всё дело в том, что действительно Bash на произвольной Linux-машине, скорее всего, будет — и, скорее всего, настроенным «из коробки» примерно одинаково (то есть почти никак). Однако это не является каким-то достоинством самой оболочки, а только данью традиции. И имеются в природе дистрибутивы Linux’а, которые этой традиции не следуют (например, Tiny Core Linux, в котором роль системной командной оболочки исполняет Ash). А уж ожидать встретить Bash установленным в произвольной BSD-системе было бы вообще опрометчиво.
Так что на самом деле единственный по настоящему универсальный инструмент для скриптинга — эвентуальный POSIX Shell, обычно представляющийся как /bin/sh
. Практическими же его воплощениями являются Ash и его Debian-модификация Dash. Именно такой подход последовательно провидится в дистрибутивах семейства Ubuntu: в них системные сценарии из каталога /etc/
содержат такой ша-банг:
#!/bin/sh
А /bin/sh
является символической ссылкой на исполняемый файл Dash, в чём легко убедиться командой
$ ls -l /bin/sh lrwxrwxrwx 1 root root 4 фев 17 2016 /bin/sh -> dash*
Ша-банг /bin/bash
в Ubuntu’идах можно встретить только в конфигах программ, не относящихся к «коренным» пакетам системы. Впрочем, насколько я знаю, такое же положение имеет место быть в Debian’е и его «чистых» клонах.
Ещё один распространённый миф — что Zsh это улучшенная версия Bash, контаминированная с возможностями Csh. Однако если обратиться к истории, становится ясно, что в 1990 году, когда Пауль Фальстад (Paul Falstad) сочинял первую версию Zsh, Bash’у от роду едва стукнул годик, и заимствовать из него было ещё нечего. А вот из оболочки TENEX C Shell, обладавшей тогда самыми развитыми интерактивными возможностями, заимствовано было многое. Так что Zsh скорее можно считать результатом адаптации Tcsh на предмет совместимости с POSIX Shell. Совместимость с конфигами Csh/Tcsh была сохранена «из уважения к памяти предков», и сохраняется до сих пор.
И последний миф, на котором мы остановимся — о какой-то сверхъестественной сложности конфигурирования Zsh по сравнению с Bash, который якобы «правильно» настроен «из коробки». Во-первых, изначальная настроенность последнего, мягко говоря, несколько преувеличена — по умолчанию она не предусматривает даже такой банальной операции, как рекурсивное копирование (без опции -r
).
Во-вторых, при первом запуске Zsh как login shell предлагается запустить сценарий автоматического конфигурирования этой оболочки, завершающийся созданием файла ~/.zshrc
, который будет включать все необходимые данному применителю опции.
И в-третьих, эффективное применение Zsh вовсе не требует каких-то страшных конфигов в сотни (а то и тысячи) строк, отыскивания их в сети или, тем более, пребывания завсегдатаем сайта Oh My ZSH! — хотя всё это и не вредно. На самом деле достаточно знать об основных уникальных фичах этой оболочки в интерактивном режиме, таких, как глобальные псевдонимы, расширенное и множественное перенаправление, расширенное автодополнение etc. А уж если ощутить в них потребность — то включить соответствующие опции в своём личном конфиге будет делом несложной техники.
А отказаться от интерактивных функций Zsh после их освоения будет психологически невозможным. Во всяком случае, мы не знаем ни одного применителя этой оболочки, который не испытывал бы дрожи при одной мысли, что враги народа заставят его работать в Bash’е.
Именно поэтому командная оболочка Zsh была принята в системе Cintu
как пользовательская регистрационная (login shell) по умолчанию. А, дабы её можно было эффективно использовать «из коробки», она сопровождается здесь конфигурационным файлом ~/.zshrc
. В нём задействована изрядная часть полезных интерактивных функций Zsh, в том числе и интеграция с менеджером пакетов apt
. Что, конечно, не запрещает применение этого конфига в любом другом дистрибутиве семейства Ubuntu. А, за вычетом строк, специфических для deb based систем — и в любой UNIX-подобной системе вообще.
Zsh в Cintu
Вкратце резюмируем причины, по которым Zsh принят в Cintu как регистрационная оболочка пользователя по умолчанию для того аккаунта, который создаётся при инсталляции системы:
- бесспорное превосходство Zsh над всеми другими шеллами при интерактивной работе;
- наличие некоторых уникальных функций, не реализованных ни в одной другой командной оболочке;
- лёгкая, вследствие этого, интеграция Zsh с менеджером пакетов
apt
.
Не все из перечисленных возможностей работают в Zsh «из коробки» — большая их часть требует соответствующей настройки. В Cintu включён главный конфигурационный файл этой оболочки ~/.zshrc
для пользователя, чей аккаунт создаётся при установке системы. В него включены все опции, необходимые для работы описываемого функционала. Файл этот по возможности прокомментирован на русском языке. А поскольку консоль в Cintu корректно русифицирована, его можно прочитать даже при крахе графической оболочки.
Оболочка Zsh в Cintu по умолчанию устанавливается как login shell также для каждого вновь создаваемого аккаунта, и наследует аналогичный конфиг из /etc/skel/.zshrc
.
Те из потенциальных применителей Cintu, которые категорически не пожелают расставаться с привычным Bash’ем, могут легко вернуть его в качестве регистрационной оболочки текущего пользователя командой
$ chsh -s /bin/bash
Однако мы с котом Manual’ом надеемся, что потенциальные применители Cintu оценят несравненные достоинства Zsh как интерактивной оболочки. И необходимости выполнять эту процедуру у них не возникнет.
Заниматься настройкой Zsh применителю Cintu сразу также не потребуется: здесь эта оболочка сконфигурирована так, чтобы основные убойные фишки были доступны «из коробки». И знакомством с ними мы сейчас и займёмся.
Подстановка команд и аргументов
Эффективное использование любой командной оболочки предполагает минимизацию нажатия клавиш пальцами. Для этого существует несколько приёмов, одни из которых — общие для всех развитых командных оболочек, другие же специфичны для Zsh. К числу первых относится, например, повторение команд и аргументов.
Для повторения последней введённой команды служит удвоенный символ восклицательного знака — !!
. Например, команда
$ ls /usr/share/fonts
выведет содержимое указанного каталога:
cmap/ fira-mono/ fira-sans/ Input-Font/ …
Если же теперь ввести
$ !!
и нажать Enter, мгновенно последует тот же самый вывод:
ls /usr/share/fonts cmap/ fira-mono/ fira-sans/ Input-Font/
Символ !!
может быть использован как аргумент другой команды — в этом случае на его место будет подставлена предыдущая команда. Например, если по забывчивости попытаться установить какой-либо пакет от имени обычного пользователя командой
$ apt install apt-file
последует сообщение об ошибке такого содержания:
E: Не удалось открыть файл блокировки /var/lib/dpkg/lock - open (13: Отказано в доступе) E: Не удалось выполнить блокировку управляющего каталога (/var/lib/dpkg/); у вас есть права суперпользователя?
Очевидно, что для установки пакета требуются привилегии администратора, которые в данной ситуации резонно разово получить командой sudo
. А чтобы не вводить вручную предыдущую конструкцию, её можно подменить !!
:
$ sudo !! sudo apt install apt-file [sudo] пароль для alv:
Существует и способ повторения аргументов введённых ранее команд, причём повторения последовательного. Так, если вслед за выполнением приведённой ранее команды
$ ls /usr/share/fonts
сразу же набрать в просто ls
последовательно клавишу Escape и клавишу с символом точки ., то в командной строке появится тот же путь, что и ранее. Причём перед отправкой команды на исполнение его можно отредактировать, например, дописав в него следующий по вложенности подкаталог:
$ ls /usr/share/fonts/Input-Font/
По исполнении этой команды первое после набора ls
нажатие Escape+. вызовет предыдущий аргумент, второе — предпоследний, то есть /usr/share/fonts
, и так далее. Однако тут надо быть внимательным: комбинации Escape+. до лампочки, к какой команде она подставляет аргументы, и со временем в строке окажется что-то совсем не подходящее, например:
$ ls apt-file
Кстати, в приведённых выше примерах вовсе не обязательно набирать команды и тем более пути к ним целиком, поскольку существует автодополнение, о котором сейчас и пойдёт речь.
Автоматическое дополнение
Во всех развитых шеллах с давних времён (хотя и не изначально) существует механизм автоматического дополнения команд и путей к файлам по нажатию клавиши табулятора. Механизм этот прекрасно известен всем линуксоидам по его реализации в Bash’е. Однако в Zsh обычное автоматическое дополнение можно дополнить парой полезных фишек — и в Cintu в пользовательском ~/.zshrc
это сделано «из коробки».
Первая из них — развёртывание аббревиатур путей в пути полные. То есть если набрать в командной строке что-то вроде
$ ls /u/sh/fo/tr/la
а затем нажать клавишу табулятора, то эта, казалось бы, бессмысленная последовательность символов волшебным образом преобразуется во вполне разумный аргумент команды:
$ ls /usr/share/fonts/truetype/lato/
Вторая Zsh-специфичная особенность автодополнения — вывод предлагаемых вариантов в виде меню-подобного списка. А именно, если после набора последовательности символов
$ ls /u/sh/fo/tr/l
нажать клавишу табуляции дважды, произойдёт не только разворачивание аббревиатуры пути, но и вывод содержимого последнего в цепочке подкаталога в виде списка, по которому, как по любому меню, можно перемещаться до нахождения нужного.
Обе эти фишки не берутся не берутся с неба, а достигаются включением соответствующих опций в ~/.zshrc
. Каких именно — будет рассказано со временем. А пока перейдём к вопросам истории (команд).
Вопросы истории (команд)
Извлечение ранее введённых команд из их «истории» — ещё один, наряду с подстановками и автоматическим дополнением, способ минимизации набора символов при работе в CLI. И эта возможность также существует по всех современных командных оболочках. Однако и тут Zsh некогда выделился дополнительными функциями (правда, ныне реализованными и в Bash, хотя там они включаются несколько иным способом).
Для начала — несколько общих слов. Всем известно, что команды в истории оных можно последовательно просматривать, нажимая клавиши Up (назад) и Dowm (вперёд). А найдя там нужную — отправить её на исполнение нажатием клавиши Enter. При необходимости предварительно отредактировав опции и (или) аргументы. Однако это может быть занятием достаточно нудным, потому что нужно учитывать следующее.
История команд, доступная для «перебора клавишами», сохраняется в Zsh как бы дважды. Во-первых, в буфере текущего сеанса шелла, запущенного в данном терминальном окне, его вкладке (если эмулятор терминала таковые поддерживает) или в текущей виртуальной консоли.
А во-вторых, история команд записывается в специальный dot-файл домашнего каталога пользователя — в Cintu таковым является ~/.zhistfile
. Причём в ней Zsh настроен таким образом, что вновь введённая команда попадает в файл истории сразу, а не по завершении текущего сеанса. То есть она будет последней в списке команд следующего сеанса, при открытии нового терминального окна или вкладки в существующем.
Так что при высоком лимите на количество строк в буфере сеанса и в файле истории (а в Cintu лимит обычно высок в обоих случаях), перебор команд может потребовать немало времени. Конечно, файл ~/.zhistfile
можно просто просмотреть, например, командой less
, дабы найти нужную, в том числе и с применением фильтра типа grep
. Однако и это не намного легче. Даже с учётом того, что в файл истории, в соответствие с принятыми в Cintu настройками Zsh, не попадают дубликаты команд и пустые строки.
Кстати, пока не забыл: предотвратить попадание команды в историю, если это почему-либо не желательно (например, её аргумент содержит учётные данные доступа ftp-серверу), можно, введя пробел перед именем команды. Однако в буфере текущего сеанса она всё равно сохранится до конца оного, и будет доступна для «перебора» — причём именно в такой, «историко-недоступной», форме.
Думаю, ни для кого не секрет также, что для вызова списка команд предназначена специальная команда history
. Однако в данном случае следует учитывать, что это — встроенная команда оболочки, зависит от последней, а потому её возможности в Bash и в Zsh различаются. И редкий случай — не в пользу последней, поскольку в Bash её функционал обеспечивается библиотекой GNU History Library, в Zsh не задействованной.
Впрочем, потеря не велика — команда history
в её Bash-модификации никогда не казалась мне очень удобной. Тем более что в Zsh есть замечательный механизм history-substring-search
— поиска в истории по начальной последовательности символов.
Что это такое — проще пояснить на примере: вы вводите в командной строке один символ (для примера — s
) и нажимаете клавишу Up. И тут в дальнейший «перебор» включаются только те команды из истории, которые с буковки s
начинаются. Вводя дополнительные символы, можно сузить круг поиска: например, последовательность sudo
позволяет просмотреть, что мы наколбасили от лица суперпользователя.
Кроме того для облегчения процедуры поиска предусмотрена такая интересная возможность, как наращиваемый поиск (incremental search). Выполняется он так: после нажатия (при пустой командной строке) клавишной комбинации Control+R появляется предложение ввести алфавитный символ (или — последовательность символов произвольной длины), заведомо входящий в состав требуемой команды:
$ bck-i-search: _
Ввод такого символа выведет последнюю из команд, его содержащих. При этом введённый символ будет отмечен знаком курсора. Он не обязан входить в имя команды, но может быть составляющим её опций или аргументов (имени файла или пути к нему, например). Следующее нажатие Control+R зафиксирует курсор на предыдущем символе, в пределах этой же или более ранней по списку команды, и т.д. Однако вместо этого в строке поиска можно вводить дополнительные символы, детализирующие условия поиска команды (или — её опций и аргументов).
Процедуру поиска можно продолжать вплоть до достижения требуемого результата — то есть нахождения той команды, которая нужна именно сейчас. Нажатие клавиши Enter в любой из этих моментов запускает найденную (то есть помещённую в командную строку) команду на исполнение, с завершением поиска. Поиск обрывается также и нажатием комбинации Control+C. Перед запуском найденная команда может быть отредактирована стандартными средствами — с использованием управляющих последовательностей.
Перебор через history-substring-search
и инкрементный поиск по Control+R отнюдь не исключают друг друга, а дополняют: первым способом проще искать ранее введённые директивы по имени команды, вторым — по её аргументам, например, по имени файла.
И ещё: и инкрементный поиск, и history-substring-search
имеется как в Zsh, так и в Bash. И в обоих оболочках первая функция работает по умолчанию, а вторая должна быть включена в конфигурационных файлах, причём по разному. Как это делается в Zsh — мы расскажем чуть позже. А включение этой функции в Bash описано, например, здесь. И справедливости ради стоит заметить, что функция history-substring-search
впервые появилась в оболочке Tcsh, где она, как правило, включена по умолчанию.
Псевдонимы обычные, глобальные и «суффиксные»
Псевдонимы для команд с их опциями (alias) — ещё один из простых способов минимизировать ввод командных директив, применяемый во всех оболочках. Способ этот обычно сводится к подмене команды с какой-либо опцией (или их набором) самим именем команды, для которой эта опция (или опции) чаще всего востребованы.
Обычно такие псевдонимы заносятся в пользовательский конфиг регистрационной оболочки, в нашем случае ~/.zshrc
. Например, псевдоним
alias mv='mv -i'
предотвращает случайное перезаписывание одноимённых файлов при перемещении или переименовании. А псевдоним
alias cp='cp -iR'
делает то же самое для команды копирования. И, кроме того, обеспечивает рекурсивное копирование содержимого каталогов. Эти псевдонимы будут подробно рассмотрены при описании конфигурационного файла ~/.zshrc
.
Однако в Zsh имеется два вида псевдонимов, которые ещё больше облегчают жизнь применителя этой командной оболочки. И, насколько я знаю, не имеющие аналогов в Bash’е и прочих shell’ах
Первый из них — так называемые глобальные псевдонимы (global alias). Всех нас раздражает ситуация, когда в ответ на попытку поиска файла утилитой find
или фрагмента текста grep
выводится множество сообщений о том, что доступ к некоему каталогу запрещён. Что выглядят примерно так:
find: «/var/cache/ldconfig»: Отказано в доступе
И среди таких сообщений, занимающих иногда многие экраны, полезная информация просто теряется.
Разумеется, каждый, кто систематически работает в CLI, знает, что для подавления этого «шума» достаточно присобачить к конструкции поиска посредством утилиты find
или grep
маленький аппендикс в виде 2> /dev/null
, перенаправляющий все сообщения об ошибках в «нулевой» файл.
Однако в Zsh борьба с «шумом» осуществляется ещё проще: достаточно задать такой глобальный псевдоним:
$ alias -g N='2>/dev/null'
где -g
указывает, что следующий символ (или символы) представляют собой не простой псевдоним, а глобальный, N
— его имя, а следующая после равенства последовательность в строгих кавычках — подменяемое им выражение. И теперь можно задавать команду поиска файлов вида:
$ find path2 -name [filename] N
и больше не заботиться об отделении «зёрен от плевел».
Глобальные псевдонимы очень полезны в командных конструкциях перенаправления по конвейеру, например, для поэкранного представления длинного вывода какой-либо команды:
$ alias -g L='|less'
А для фильтрации вывода по вхождению последовательности символов можно задать такой глобальный псевдоним:
$ alias -g G='|grep'
После чего использовать его в конструкциях, например, поиска пакетов:
$ apt search apt G '^apt-'
В сочинительском труде весьма полезен такой глобальный псевдоним:
$ alias -g W='|wc -m'
Ибо нашему брату часто требуется прибегать к такой конструкции:
$ cat filename W
Она в данном случае выведет число символов в текстовом файле — что для нас важнее числа байт (а при использовании 16-битной кодировки для преимущественно кириллического текста эти значения очень не совпадают).
Второй вид уникальных псевдонимов в Zsh — псевдонимы «суффиксные» (более удачного определения их я не придумал). Подобно тому, как добавление к команде alias
опции -g
превращает обычный псевдоним в глобальный, так и опция -s
делает его «суффиксным». То есть привязывает суффикс имени файла (в просторечии часто называемый «расширением») к некоей программе, которая может сотворить над ним нужное действо. Например, если задать псевдоним такого вида
$ alias -s txt='nano'
а затем набрать в командной строке имя указанного текстового файла (при необходимости с указанием пути к нему)
$ path2/filename.txt
то этот самый файл будет открыт в текстовом редакторе Nano.
Это, конечно, банальщина. Но есть и более интересное применение «суффиксных» псевдонимов. Например, с их помощью можно просматривать содержимое пакетов (применительно к нашему случаю — *.deb
). Для чего надо задать такой псевдоним:
$ alias -s deb='dpkg-deb -c'
И теперь достаточно набрать в командной строке полное имя deb-файла:
$ path2/hunspell-ru-aot_0.4.0-2_all.deb
чтобы сразу получить полный список входящих в него файлов:
drwxr-xr-x alv/alv 0 2016-09-19 11:47 ./usr/ drwxr-xr-x alv/alv 0 2016-09-19 11:47 ./usr/share/ drwxr-xr-x alv/alv 0 2016-09-19 17:58 ./usr/share/hunspell/ -rw-r--r-- alv/alv 279246 2016-09-19 11:47 ./usr/share/hunspell/ru_RU.aff -rw-r--r-- alv/alv 4695673 2016-09-19 11:47 ./usr/share/hunspell/ru_RU.dic ... lrwxrwxrwx alv/alv 0 2016-09-19 17:44 ./usr/share/myspell/dicts/ru_RU.aff -> /usr/share/hunspell/ru_RU.aff lrwxrwxrwx alv/alv 0 2016-09-19 17:44 ./usr/share/myspell/dicts/ru_RU.dic -> /usr/share/hunspell/ru_RU.dic
Разумеется, определения всех постоянно используемых псевдонимов, и глобальных, и «суффиксных», также не обязательно задавать каждый раз в командной строке, как в приведённых выше примерах — их резонно занести в файл ~/.zshrc
. Что, не можем удержаться от очередного напоминания, и сделано в Cintu, что называется, «искаропки». То есть применителю этой системы, если он не откажется от использования Zsh в качестве login shell, не придётся заморачивать себе голову вводом ни одного из приведённых выше псевдонимов. Как, впрочем, и многих других, которые ему потребуются в жизни: всё учтено могучим ураганом.
Жизнь без команд
Казалось бы, CLI, обеспечиваемый командными оболочками, предназначен для ввода команд. Однако командная оболочка Zsh, вопреки своему названию, позволяет обходиться привычных команд. Не без всех, конечно, а только немногих. Но зато принадлежащих к числу самых употребимых.
Она из таких команд, необходимость в которой при использовании Zsh отпадает полностью — cd
, служащая, как известно, для перехода в каталог, указанный в качестве её аргумента. Как это может быть?
Очень просто. Вспомним, что переход в некий каталог для этого типа файлов (именуемого directory) — ни что иное, как исполнение для обычного (ordinary) файла. И потому вполне логично, что как для запуска, например, скрипта оболочки не требуется никакой специальной команды, так и для перехода в каталог, к которому данный пользователь имеет доступ (то есть для него этот каталог имеет бит исполнения), ему достаточно указать полный путь к нему, без всяких команд. Например, введя к командной строке что-нибудь вроде
$ /usr/share/fonts/
он немедленно оказывается в каталоге со шрифтами.
«Беcкомандный» переход в каталоги распространяется и на «символические» обозначения последних. Так, команда
$ ~
переместит пользователя в его домашний каталог. Как, кстати, и команда, а директива
$ ..
в — каталог, родительский относительно текущего. Если же в командной строке ввести прямой слеш
$ /
то произойдёт перемещение в корень файловой иерархии.
Однако это ещё не всё — в некоторых случаях для смены можно обойтись не только без команды cd
, но и без указания полного пути. Например, если набрать просто слово
$ Download
и даже не обязательно целиком (достаточно Dow
и нажать табулятор), то, вне зависимости от текущего положения, пользователь окажется в одноимённом подкаталоге своего домашнего каталога. Или, если он предпочитает локализованные имена, то в каталоге ~/Закачки
.
По умолчанию такой «беспутёвый» способ перехода работает только для стандартных подкаталогов домашнего каталога пользователя (таких, как ~/Pictures
, ~/Documents
и так далее). Однако, как будет показано в разделе о настройках Zsh, никто не запрещает распространить его на другие часто посещаемые каталоги.
И кстати: как известно, для определения текущего положения в файловой иерархии предназначена специальная команда pwd
. Однако в Zsh и без неё можно обойтись с помощью настройки приглашения командной строки, о чём тоже будет говориться в соответствующем месте.
Ещё одна из часто используемых команд CLI — less
,относимая к семейству так называемых пейджеров (не путать с тем, чем лохи в песочницах ковыряются). То есть программ поэкранного просмотра файлов (преимущественно текстовых), в число которых входят также ветхозаветная утилита more
и некогда пропагандируемая в качестве прогрессивной программа most
.
Так вот, в Zsh все они оказываются излишними — эта командная оболочка справляется с пейджингом текстовых файлов сама. Например, такая конструкция, как
$ < ~/.zshrc
выведет на экран содержимое главного конфига нашей оболочки. Которое, как и при выводе через, например less
, можно пролистывать поэкранно клавишами PgDown и PgUp, просматривать построчно стрелками Down и Up, отыскивать нужные последовательности символов с помощью /что_надо
, повторять поиск вперёд или назад с помощью клавиш n и N, соответственно.
Однако это ещё не всё. С помощью того же оператора <
можно просмотреть два (и более) файла в одном потоке. Так, конструкция
$ < ~/.{zshrc,zshenv}
выведет на экран последовательно содержимое обоих основных конфигов оболочки Zsh. Кстати, шаблоны файлов в этой конструкции также не запрещены, так что можно поступить ещё проще:
$ < ~/.zsh*
Однако и это ещё не предел возможностей: если вывод нескольких файлов перенаправить в новый файл, то реализуется вековая мечта сочинителей всех времён и народов. То есть из нескольких отдельно написанных фрагментов (скажем, статей) создаётся единый текст (предположим, диссертации):
$ <chapter[01-10] > mybook
Всё это обеспечивается в Zsh особыми механизмами перенаправления — расширенным и множественным, о которых подробней будет говориться в другом месте.
А пока заметим, что оба вида перенаправления в Zsh заменяют не только команду less
(и аналогичные пейджеры), но и команду cat
: для неё остаётся только использование по прямому назначению, для объединения (конкатенация) файлов.
Существуют и другие области использования расширенного и множественного перенаправления. Однако и сказанного, думаю, достаточно для осознания полезности этих механизмов. Как и командной оболочки Zsh вообще. Так что применителям дистрибутивов, отличных от Cintu, и использующим Bash, можно только посочувствовать.
О конфигах Zsh вообще
В разделе о мифологии относительно Zsh упоминалось про изобилие конфигурационных файлов этого шелла. И говорилось, что большая их часть совсем не обязательна для использования и, тем более, редактирования.
Однако кот Мануал решил вкратце остановиться на этом вопросе — во-первых, полноты картины для. А во-вторых и главных — дабы потенциальный применитель Zsh, прочитав в сети (или в документации) о конфигурационных файлах этой оболочки, не устрашился бы их изобилия. А сразу понял бы, что из него ему требуется, а что — можно (и нужно) хладнокровно проигнорировать.
Конфиги Zsh
В Zsh предусмотрен следующий набор общесистемных конфигурационных файлов, находящихся каталоге /etc/zsh
:
zshenv
— считывается при запуске любого экземпляра Zsh;zprofile
— используется только при запуске Zsh как регистрационной оболочки входа (login shell);zshrc
— обрабатывается при запуске любого интерактивного экземпляра Zsh;zlogin
— используется только при запуске как login shell.
Это — в теории. Практически набор общесистемных конфигов зависит от конкретной сборки пакета zsh
, и различается в разных системах. Например, в Antergos’е имеется только один общесистемный конфиг Zsh — /etc/zsh/zprofile
. А в сборках этой оболочки для Ubuntu’идов, напротив, имеется ещё /etc/zsh/newuser.zshrc.recommended
, к которому мы вернёмся позднее.
Опять же теоретически общесистемные конфиги Zsh могут дополняться своими пользовательскими тёзками — файлами ~/.zshenv
, ~/zprofile.
, ~/.zshrc
и ~/.zlogin
. Практически, как будет сказано далее, для полноценной работы достаточно одного из них — ~/.zshrc
: только он и создаётся при автоматическом конфигурировании оболочки. Созданием прочих конфигов применитель должен озаботиться сам. Если это ему на самом деле это необходимо, в чём я лично сильно сомневаюсь — за исключением некоторых специальных случаев.
Содержимое и назначение конфигов
Как уже сказано, все системные конфиги Zsh из каталога /etc/zsh
имеют (точнее, могут иметь) свои аналоги в домашнем каталоге пользователя. И назначение их следующее:
zshenv
и его «домашний тёзка» содержат только определения путей (переменная$PATH
) и некоторые другие переменные окружения; в нём не должно быть команд, выводящих что-либо, и привязки к терминалам;zprofile
в «апстримовском» варианте (и в Ubuntu’идных сборках) по умолчанию просто пуст; в Antergos’е он явным образом вызывает системный/etc/profile
, общий для всех POSIX-совместимых оболочек;zshrc
и его «домашний дубель» содержат все настройки шелла, важные для альтернативной работы; первый по умолчанию пуст, а о содержимом второго будет сказано позднее;zlogin
и~/.zlogin
служат для определения таких вещей, как тип терминала, а также для запуска команд, выполнение которых необходимо сразу после входа в систему (если таковые имеются).
При запуске Zsh как регистрационной оболочки конфиги считываются в определённом порядке, при котором настройки из домашнего каталога имеют приоритет над общесистемными:
zshenv
->~/.zshenv
zprofile
->~/.zprofile
zshrc
->~/.zshrc
zlogin
->~/.zlogin
Разумеется, это происходит в том случае, если Zsh в системе установлен с полным набором конфигов, что на самом деле бывает очень редко.
Кроме файлов, считываемых при запуске шелла, имеется и конфиг, обрабатываемый при выходе из интерактивного пользовательского сеанса по команде logout
(или exit
). Это — системный zlogout
(по умолчанию пустой), который может иметь (или не иметь) «домашний» аналог, ~/.zlogout
.
В домашнем каталоге пользователя, кроме «дублей» присутствует два «уникальных» dot-файла и вот их-то наличие непременно: ~/.zhistory
(или .histfile
) хранит историю команд, а ~/.zcompdump
— представляет собой своего рода кеш автодополнений. Два последних файла возникают сами собой, и немало способствуют эффективности использования Zsh.
Зачем из столько?
Настало время задаться вопросом: а зачем Zsh так много файлов настройки? Ведь обычные стиральные порошки шеллы спокойно обходятся двумя (а то и одним /etc/profile
, как легендарный /bin/sh
). Тем более, что «сладкие парочки» zprofile
и zlogin
по назначению явно дублируют друг друга.
Для ответа на поставленный вопрос нужно вспомнить о трёх режимах функционирования любого шелла — неинтерактивном, интерактивном и подвиде последнего — регистрационном (то естьlogin shell
). А как было сказано выше, файлы zshenv
и ~/.zshenv
считываются при каждом запуске любого экземпляра Zsh, в том числе и исполняемого в скриптах. А настройки из файлов zshrc
и ~/.zshrc
имеют силу для любого интерактивного запуска (например, в иксовом терминале).
Наконец, файлы zlogin
и zprofile
, вместе с сопутствующими им ~/.zlogin
и ~/.zprofile
) обеспечивают настройку только того экземпляра Zsh, который был запущен после регистрации пользователя в системе. То есть, вроде бы, не охватывают те, сеансы шелла, которые запускаются в терминальных окнах. Впрочем, с важной оговоркой: нынче большинство «продвинутых» терминальных программ имеют в своих настройках опцию Запускать как login shell.
Всё это теоретически позволяет гибко (и индивидуально) настроить экземпляры Zsh, запускаемые из сценариев, обычные интерактивные сеансы и сеанс регистрационный. Правда, а зачем нам два генеральных секретаря конфига для login shell
? — спросите вы меня. Ответ прост — из соображений совместимости с Bash и Tcsh. Для пояснения чего обратимся к истории командных оболочек.
В первозданном шелле Борна и развивающем его шелле Корна существовал только один конфиг — profile
(~/.profile
) для любых экземпляров шелла. В Bash к нему прибавился ещё и файл bashrc
(~/.bashrc
) для интерактивного использования (считываемые, естественно, после предыдущего — как более молодые по происхождению).
В Csh был совсем иной набор. Там изначально существовали два конфига — csh.env
(~/.csh.env
) на все случаи жизни и /etc/login
(~/.login
) — в качестве конфигуратора login shell
, считываемые в указанном порядке.
В Zsh, дабы удовлетворить привычки пользователей любых предшествовавших шеллов, были включены оба набора конфигов, причём порядок их считывания был унаследован от каждого из родителей. В результате получилась довольно сложная последовательность при запуске login shell
, описанная выше.
На практике нынче всё описанное выше богачество конфигов оказывается невостребованным. И пользователь вполне может обойтись только одним ~/.zshrc
в своём домашнем каталоге — ведь login shell
по определению интерактивен. Общие же переменные окружения (такие, как $PATH
) будут извлекаться из общесистемного конфига /etc/profile
, который считывается всеми POSIX-совместимыми шеллами. Именно так мы и поступим.
Что настраивать
Разобравшись с назначением dot-файлов, можно заняться отделением зёрен от плевел, то есть решить: а какие же именно файлы нужно настроить.
Для начала мы оставим бы в покое все файлы из каталога /etc
, которые вполне могут быть перезаписаны при обновлении системы. Так что ограничимся только нашим домашним каталогом. Однако здесь кое-что зависит от дистрибутива.
Так, в Ubuntu’идах (и, видимо, вообще во всех deb-based системах) по умолчанию не работает такая полезная фича, как history-substring-search
— поиск в истории по начальной последовательности символов. И один из способов устранения этого недостатка — использование специальной опции в файле ~/.zshenv
, который нам и потребуется в Cintu.
Далее, решаем, а нужен ли нам отдельный конфиг для login shell
, и если нужен — то какой из них, ~/.zlogin
или ~/.zprofile
. И приходим к выводу, что вполне можно обойтись и без того, и без другого, поскольку применяться Zsh будет в основном в терминальных окнах. По той же причине представляется лишним и ~/.zlogout
.
И в итоге остаётся главный (по крайней мере, самый большой) конфиг Zsh — ~/.zshrc
, определяющий поведение этой оболочки в интерактивном режиме. Что с ним делать? Тут, как говорится, возможны варианты.
Способы настройки своего zshrc
После установки Zsh, и даже после его назначения любимой женой регистрационной оболочкой никакие пользовательские конфиги в домашнем каталоге не образуются. И при попытке первого входа в сеанс шелла (например, в терминальном окне) запускается его интерактивный конфигуратор:
Как можно видеть на скриншоте, он предлагает три направления дальнейших действий:
- прерывание конфигурирования — в этом случае предложение заняться им поступит при следующем запуске шелла (например, при открытии терминального окна или вкладки в нём);
- завершение конфигурирования с созданием пустого
~/.zshrc
, что предотвратит запуск конфигуратора при открытии следующего сеанса; - продолжение процесса конфигурирования.
Третий вариант кот Манул предполагает рассмотреть как-нибудь позднее, опять же полноты картины ради. Пока же заметим только, что он отнюдь не рассчитан на начинающих применителей Zsh — результата, оправдывающего затраченное на него время, можно достигнуть, только понимая смысл совершаемых действий — а для этого понимания требуется некоторый опыт.
Можно, конечно, просто соглашаться со всеми предлагаемыми конфигуратором умолчаниями — и в результате получить если и не идеальный ~/.zshrc
, то весьма разумный и функциональный. Однако тот же результат достигается проще, о чём будет сказано далее.
Второй путь, приводящий к созданию ~/.zshrc
с единственной строкой комментария, молчаливо предполагает, что остальное его содержание будет вноситься руками, в текстовом редакторе, из головы (или из документации). Что, понятно, не самое простое дело, и не только для начинающего, но и для многоопытного применителя.
И поэтому мы с котом идём другим путём, первым: прерываем процесс конфигурирования, и копируем в свой домашний каталог свой ~/.zshrc
. Ну у нас-то с Мануалом таковой имеется вот уже более пятнадцати лет. А что делать применителям, кто запускает Zsh первый раз в жизни? Тут на их пути возникает развилка из трёх, опять-таки, дорог.
В начале этого очерка упоминался файл /etc/zsh/newuser.zshrc.recommended
. Как нетрудно догадаться по его имени, это и есть конфиг Zsh, рекомендуемый для нового пользователя. И выбор первого ответвления нашего пути — просто скопировать его в свой домашний каталог:
$ cp /etc/zsh/newuser.zshrc.recommended ~/.zshrc
Результат будет примерно тот же, что и при прохождении всех стадий автоматического конфигурирования, соглашаясь со всеми умолчаниями. То есть: разумный, но не идеальный. Однако это путь самый быстрый и простой — а поисками идеала заняться на досуге.
Тем не менее, устремление к идеалу можно и не откладывать в долгий ящик. А поискать его среди конфигов, в изобилии представленных как в дополнительных пакетах вроде zsh-lovers
, так и на многочисленных тематических ресурсах, из которых наибольшей известностью пользуется Oh My ZSH!. Однако интересных материалов и в первом, и во втором случае так много, что в нём легко утонуть с головой. И забыть, зачем всё это начиналось.
И потому мы с Мануалом предлагает пойти по третьему ответвлению первого пути, а именно: скачать и скопировать наш ~/.zshrc
. Мы, конечно, не настолько самоуверенны, чтобы считать свой конфиг Zsh лучшим конфигом всех времён и народов. Однако он обеспечивает весь ранее опписанный функционал этой оболочки.
Самое же главное — наш ~/.zshrc
, безусловно, лучше всего описан на русском языке. В частности, здесь — «читайте меня», как говаривал Бернард наш, Шоу. Кстати, и сам конфиг внутри содержит достаточно подробные комментарии, и тоже на языке родных осин.
Авторский ~/.zshrc
И здесь кот Мануал велел мне поделиться нашим с ним главным dot-файлом для Zsh — ~/.zshrc
, определяющего поведение этого шелла, запущенного в интерактивном режиме — как было сказано ранее, в большинстве случаев для настройки его достаточно.
Вступление
Представляемый ~/.zshrc
существует с 2001 года, кочуя с машина на машину, из системы в систему, постоянно модернизируюсь в соответствие с изменениями как возможностей Zsh, так и наших в них потребностей. В текущем своём состоянии он обеспечивает все необходимые для нас функции CLI. Хотя, разумеется, не охватывает всех возможностей Zsh.
Данный конфиг может быть использован полностью или фрагментарно всеми заинтересованными лицами. Далее в тексте страницы блоки, заключённые в теги <pre></pre> (то есть выводимые моноширинным шрифтом), пригодны для прямого копирования. А связующие блоки, визуально воспроизводимые шрифтом пропорциональным, могут рассматриваться как комментарии.
Весь конфиг разделён нами на несколько секций, объединяющих группы взаимосвязанных опций. Сделано это исключительно для структурирования материала, и ни в коем случае не обязательно. Как, в общем-то, и порядок строк в конфиге.
Мы с котом Мануалом отнюдь не призываем к механическому «копипасту» данного конфига, напротив: настоятельно рекомендуем, используя его и аналоги, которые можно найти в Сети, по мере сил и возможности создавать конфиг собственный. Ибо хороший (для конкретного применителя) ~/.zshrc
— это не результат, а процесс, и причём процесс преувлекательный.
Вводная секция
Вводная секция ~/.zshrc
— несколько строк комментариев, сообщающих, что этот конфиг используется только в интерактивных экземплярах Zsh. Он содержит команды для определения псевдонимов, функций, опций и кейбиндингов. А заодно напоминает последовательность считывания конфигов при запуске оболочки:
# zshenv, zprofile, zshrc, zlogin
Здесь же сообщается, что версии этого конфига созданы котом Manual’ом (при участии вашего покорного слуги) для использования, в первую очередь, в Cintu. Но также — во всех прочих дистрибутивах, которые мы иногда применяем по жизни. С поправкой на специфику, конечно.
Опции истории команда
Определение имени файла истории (по умолчанию ~/.histfile
):
HISTFILE=~/.zhistfile
Указание числа команд, сохраняемых в буфере сеанса, и числа команд, сохраняемых в файле истории:
HISTSIZE=2000 SAVEHIST=10000
Опции, определяющие условия сохранения команд в файле истории.
Инкрементное наращивание файла истории:
setopt INC_APPEND_HISTORY
Удаление дубликатов команд:
setopt HIST_IGNORE_ALL_DUPS
Удаление из файла истории пустых строк:
setopt HIST_REDUCE_BLANKS
Удаление строк, начинающихся с пробела:
setopt HIST_IGNORE_SPACE
Определение горячих клавиш (key bindings) доступа к истории команд.
Включение history-substring-search
— поиска в истории по начальной последовательности символов:
bindkey "^[[A" up-line-or-search bindkey "^[[B" down-line-or-search
Примечание: в Ubutnu’идах (и, насколько я знаю, во всех deb-based системах вообще) субстринговый поиск по умолчанию не работает. Проблема решается несколькими способами. В Cintu — путём автоматического создания файла ~/.zshenv
, о содержании которого будет сказано в конце Главы.
Пролистывание истории клавишами PageUp и PageDown:
bindkey "^[[5~" up-line-or-history bindkey "^[[6~" down-line-or-history
Исправление работы клавиш Home, End, Delete (заменить кодами клавиш своего терминала или текстовой консоли):
bindkey "^[OH" beginning-of-line bindkey "^[OF" end-of-line bindkey "^[[3~" delete-char
Примечание: определение кода клавиши в терминале (консоли) выполняется с помощью комбинации клавиш Control+V [клавиша] или командой
$ cat [Enter] [клавиша]
Переход на «большое» слово вперёд и назад по Control+Right/Left:
bindkey "^[[1;5C" forward-word bindkey "^[[1;5D" backward-word
Удаление от позиции курсора до конца «большого» слова:
bindkey "^[[3;5~" delete-word
Навигация и автодополнение
Отключение поддержки старой системы настройки compctl
:
zstyle ':completion:*' use-compctl false
Включение перехода в каталог без использования команды cd
:
setopt autocd
Включение перехода в указанные подкаталоги без введения полного пути
cdpath=(/home/data/alv.me/ /home/data/cinia /home/data/media/)
Менюобразный вывод вариантов по клавише Tab:
setopt menucomplete zstyle ':completion:*' menu select=1 _complete _ignored _approximate
Результат после набора, например, символа g
и нажатия клавиши табуляции выглядит таким образом:
Автодополнение псевдонимов:
setopt completealiases
Развёртывание сокращённого пути (типа /h/p/m
) до полного (/home/proj/manuals
):
autoload -Uz compinit compinit
Расширенные подстановки и дополнения:
setopt extendedglob nomatch notify zstyle ':completion:*' completer _expand _complete _ignored _correct _approximate
Отключение чувствительности к регистру:
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
Кеширование команд для автодополнения:
zstyle ':completion:*' use-cache on zstyle ':completion:*' cache-path ~/.zsh/cache
Автоматическое выполнение команды rehash
после установки нового пакета::
zstyle ':completion:*' rehash true
Определение путей к библиотекам функций:
fpath=($fpath ~/.zsh/func)
Включение кеширования для автодополнения аргументов команд управления файлами (работает, например, для apt
):
zstyle ':completion::complete:*' use-cache 1
Определение обычных псевдонимов
Псевдонимы для команд перемещения, копирования и удаления файлов.
Запрос подтверждения на перезапись одноимённых файлов при переименовании или перемещении:
alias mv='mv -i'
Рекурсивное копирование с запросом на перезапись одноимённых файлов при переименовании или перемещении:
alias cp='cp -iR'
Принудительное рекурсивное копирование:
alias cpr='cp -fR'
Удаление файлов в текущем каталоге с подтверждением:
alias rm='rm -i'
И без оного:
alias rmf='rm -f'
Рекурсивное удаление с подтверждением:
alias rmr='rm -R'
Принудительное рекурсивное удаление:
alias rmrf='rm -Rf'
Псевдонимы для опций команды ls
. Выводить каталоги перед файлами:
alias ls='ls -F --group-directories-first'
Вывод в «длинном» формате с «человечьим» размером файлов:
alias ll='ls -lh'
Вывод всех файлов, включая скрытые, текущий и родительский каталоги:
alias la='ls -A'
Вывод всех файлов в «длинном» формате с указанием inode
:
alias li=’ls -ial’
Псевдонимы для опций некоторых часто используемых команд, например, history
:
alias h=history
Вывод команд df
и du
в «человечьем» формате:
alias df='df -h' alias du='du -h'
Так называемый more
-подобный вывод команды less, с указанием полного пути и имени файла:
alias less='less -M'
Что «в натуре» выглядит так:
Закачка файла по ftp с продолжением:
alias wget='wget -c'
Запуск редактора nano
в режиме «мягкого» переноса слов (softwrap
):
alias nano='nano -$'
Вывод числа символов в текстовом файле:
alias wcm='wc -m'
«Суффиксные» псевдонимы
Определяют команды и их опции, которые должны открывать файлы с указанными опциями, например, текстовых файлов — для редактирования в nano
:
alias -s txt='nano'
Просмотр содержимого deb-пакетов:
alias -s deb='dpkg -c'
Просмотр содержимого tar-архивов, сжатых утилитами gzip
и xz
, соответственно:
alias -s gz='tar xzvf' alias -s xz='tar xJvf'
Глобальные псевдонимы
alias -g N='2>/dev/null' alias -g L='|less' alias -g G='|grep' alias -g W='|wc' alias -g H='|head' alias -g T='|tail'
Интеграция с APT
Глобальные псевдонимы для субкоманд утилиты apt
.
Информация о пакетах (достаточно прав пользователя):
alias -g se='search' alias -g sw='show' alias -g liin='list --installed' alias -g liup='list --upgradable' alias -g pol='policy'
Операции с пакетами (требуют прав root’а):
alias -g in='install' alias -g up='update' alias -g ug='upgrade' alias -g pur='purge'
Приглашения командной строки
Так называемое «функциональное» приглашение.
Активизация promptinit
:
autoload -Uz promptinit promptinit
Загрузка функции из каталога /usr/share/zsh/functions/Prompts/
:
prompt adam2
Для администратора в файле /root/.zshrc
эта строка выглядит так:
prompt adam2 red red red
Что показано на скриншоте:
Это позволяет визуально отличать пользовательский сеанс от сеанса с административными привилегиями, полученными командой sudo -i
Разные опции
Отключение выхода по Control+D:
setopt IGNORE_EOF
Отключение звукового сигнала:
setopt NO_BEEP
Поведение клавиш в стиле Emacs (чисто для проформы, по умолчанию оно такое и есть):
bindkey -e
Пейджер по умолчанию:
export PAGER="less"
Редактор по умолчанию:
export EDITOR="nano"
Содержимое файла ~/.zshenv
Файл необходим для поддержки опции history-substring-search
в Cintu (и других deb-based системах). Должен содержать такую строку:
DEBIAN_PREVENT_KEYBOARD_CHANGES=yes
Заключение
В Cintu и прочих Ubuntu’идах оба конфига, и .zshrc
, и .zshenv
можно использовать as is. Разумеется, при желании можно изменить вид приглашения командной строки, например, на одно из имеющихся в каталоге /usr/share/zsh/functions/Prompts
. Запустив предварительно команду
$ prompt -p
И выбрав подходящее в её выводе.
Мы с котом Мануалов постоянно вносим в .zshrc
всякие мелкие дополнения — по мере осознания полезности соответствующих функций. Когда их накопится достаточно — описываем новые версии. Или вносим изменения и дополнения в уже публиковавшиеся (что вряд ли, потому как забываем).
7 thoughts on “Книга о Cintu. Часть 2. Применение системы. Глава 8. Командная оболочка Zsh”
Алексей, добрый день!
Лишние два слова в конце предложения (в конце статьи):
«Когда их накопится достаточно — описываем новые версии новые версии.»
Ещё раз спасибо! Ща поправлю.
Алексей, приветствую!
А что за цифири 0 и 2 после фразы «Результат после набора, например, символа g и нажатия клавиши табуляции выглядит таким образом:»
над и под картинкой?
Спасибо, тёзка!
Ща уберу — это остатки жизнедеятельности.
Точнее, разметки исходного текста материала под размещение картинок.
Обычно убирается без следа — а тут что-то глюкнуло.
Или я что-то не то сделал 🙂
Ага, точно — ссылка на картину, вместо чтобы заменить свой номер в разметке, вклинилась аккурат между двумя его цифрами.
Ещё раз спасибо — за сигнал: буду смотреть, возможно, не только здесь…
Дядь Лёш! Да не за что. Когда пишешь много, да ещё с примерами и картинками, глаз «замыливается»…
Читаю с превеликим удовольствием, некоторые вещи оставляю себе на заметочку.
Я вот свою Kubuntu перепелил в Cintu на ходу. Пока только положительные эмоции.
На счёт замыливания — это точно. Особенно когда глаза со временем лучше не делаются 🙂
А с Cintu — рад. Я так сейчас на другую среду, кроме Cinnamon, уже и смотреть не могу. То есть смотреть — могу, но со стороны…
Особенно в ней радует, что развивается без революционных потрясений. Но в каждой новой версии хоть кая-то мелочь, а становится лучше…