Говорим на UNIX, часть 2: больше работы головой - меньше руками
сокращения оболочки помогают экономить усилия и время
Уровень: средний
Оригинал здесь:
Speaking UNIX, Part 2: Working smarter, not harderПеревод мой, весьма вольный, с сокращениями и добавлениями
Не ломай пальцы, дай им отдохнуть
Если вы ознакомились с первой частью статьи, вы уже должны были проникнуться уважением к той мощи и гибкости, которую предоставляет в ваше распоряжение командная строка оболочки. Рассмотрим команду, которая находит все текстовые документы в вашей домашней директории, содержащие фразу "Montly Report" (ежемесячный отчёт):
$ find /home/joe -type f -name '*.txt' -print | xargs grep -l "Monthly Report"
Команда "find /home/joe" просматривает вашу домашнюю директорию в поисках обычных файлов (-type f) оканчивающихся на ".txt". Всё найденное передаётся на вход утилите xargs, которая для каждого из полученных имён файлов запускает утилиту grep, выполняющую поиск упоминания в документе месячного отчёта.
Это несомненно полезная команда, однако запомнить её не слишком просто, а ещё неприятнее печатать так много букв, особенно если приходится делать это часто. И если командная строка станет вашим основным интерфейсом для решения повседневных задач, имеет смысл подумать над экономией времени и сил на вводе команд.
Поддержка облегчения ввода и экономии нажатий клавиш со стороны современных командных оболочек реализована многими механизмами:
- Sigils, спецсимволы и их сочетания
- Wildcards, символы - джокеры, или метасимволы
- история команд
- переменные окружения
- Aliases, алиасы или псевдонимы
- файлы начальной загрузки оболочки
Например, вы можете ссылаться на свою домашнюю директорию с помощью спецсимвола "~" (тильда). Другой способ сослаться на дом.дир. - использовать переменную окружения $HOME:
$ whoami
strike
$ echo ~
/Users/strike
$ echo $HOME
/Users/strike
$ !!
echo $HOME
/Users/strike
Последняя команда - два восклицательных знака - выглядит несколько непривычно. Это спец. сокращение для работы с историей команд (в zsh), вызывает последнюю из выполненных команд. Многие оболочки позволяют прокручивать историю используя клавиши-стрелки или комбинации типа Ctrl+P.
Сейчас мы рассмотрим каждый из этих механизмов ускорения работы поподробнее. Примеры в статье используют Z shell, которая обычно устанавливается по адресу /bin/zsh (пр.пер.: а у меня в /usr/bin/zsh). Свои особенности у zsh конечно есть, но примеры должны работать в большинстве современных оболочек.
Спецсимволы (sigils)
Некоторые аргументы используются в командной строке столь часто, что для них были специально придуманы символы-заменители или спецсимволы. При этом вам достаточно напечатать этот символ вместо аргумента.
Как уже упоминалось выше, тильда (~) ссылается на вашу домашнюю директорию. Похожее сокращение, ~username, ссылается на домашнюю директорию пользователя username. И, чтобы скопировать файл из дом.дир. joe в свою (в поддиректорию info) вы можете использовать такую запись:
$ cp ~joe/doc/report.txt ~/info
===== врезка: проверка =====
Если вы решите посмотреть как и на что спецсимволы будут заменены перед выполнением команды, используйте echo:
$ echo ~joe/doc/report.txt ~/info
/guests/joe/doc/report.txt /staff/bobr/info
$ echo $SHELL
/bin/zsh
$ ls
architecture.txt Services.pdf
services.txt Schema.pdf
$ echo *.txt
architecture.txt services.txt
echo просто выдаёт свой аргумент на stdout. Однако оболочка производит большинство своих манипуляций с командной строкой ещё до запуска команды на выполнение. Таким образом все подстановки становятся видны.
===== конец врезки =====
Ещё один ценный спецсимвол -- две точки (..) (не путать с двоеточием, :). Две точки обозначают директорию, которая находится непосредственно над текущей рабочей. Одна точка означает текущую рабочую директорию. С помощью двух этих сокращений удобно ссылаться на директории и файлы, указывая их пути относительно текущего.
Например, вы сейчас в директории ~/jane/projects/lambda, сокращение "../.." укажет на два уровня выше, т.е. ~/jane. Чтобы указать на директорию, в которой расположена ~/jane, надо использовать "../../../" ("тремя уровнями выше") или так: "~jane/../". Последний путь начинает отсчёт с ~jane, а затем поднимается на один уровень вверх.
Чтобы скопировать файл в текущую директорию, вам не придется вспоминать её имя. Просто используйте точку:
$ cp -pr /path/to/lots/of/stuff .
Если путь начинается с точки или двух, он считается относительным. И наоборот, если путь начинается с косой черты (/) или с тильды (~), он считается абсолютным, поскольку точно определяет положение файла в системе независимо от того, где вы сейчас находитесь.
Метасимволы и шаблоны
Подобно тому как спецсимволы облегчают работу по указанию путей в конкретные директории, метасимволы и шаблоны облегчают работу с содержимым этих директорий.
Пусть у вас есть директория, и в ней 100 файлов. Там есть исходники на языке Си (.c), объектные файлы (.o), текстовые (.txt), скрипты (.sh), а также исполняемые файлы (без расширения в имени но имеюшие разрешение на выполнение). Чтобы просто перечислить Си-файлы, набираем:
$ ls *.c
Метасимвол * обозначает соответствие любому количеству любых символов. Таким образом, оболочка будет выбирать файлы, имена которых состоят из любого количества сиволов, но оканчивающиеся обязательно на ".c". Найдя все такие файлы в текущей директории, оболочка передаст список команде ls и запустит её на выполнение.
Следующий листинг иллюстрирует работу шаблона на примере директории с исходными текстами wget, cli-утилиты для скачивания файлов:
$ ls *.c
alloca.c
ansi2knr.c
cmpt.c
connect.c
convert.c
...
Процесс замены шаблона на список соответствующих этому шаблону файлов называется глоббингом (globbing) или просто подстановкой. Обычно оболочки имеют набор операторов (метасимволов) для осуществления глоббинга, так называемых глобов (globs):
- глоб * соответствует любому набору символов, включая пустое множество.
- глоб ? соответствует любому одному символу.
- глоб [] соответствует любому одному символу из числа находящихся внутри этих квадратных скобок. Также внутри скобок вы можете задать некоторый диапазон символов. Для этого нужно использовать знак минус (-). [a-z] будет соответствовать любой букве английского алфавита в нижнем регистре.
Z shell имеет несколько уникальных глобов, о которых рассказано во врезке ниже.
===== врезка: глобы Z shell =====
Глоб **/ соответствует списку всех директорий ниже текущей и включает текущую директорию. Если мы вернемся в каталог с текстами wget, то найти все файлы по имени Makefile можно так:
$ echo **/Makefile
Makefile doc/Makefile po/Makefile
src/Makefile util/Makefile
windows/Makefile
Если вы не хотите включать текущую директорию в список обработки, попробуйте так:
$ echo */**/Makefile
doc/Makefile po/Makefile
src/Makefile util/Makefile
windows/Makefile
Ещё один удобный глоб позволяет задавать соответствие типа файла:
*(.) - обычные файлы
*(/) - директории
*(*) - исполняемые файлы
*(@) - символьные ссылки
$ ls -d -F *(/)
ChangeLog-branches/ doc/ po/ src/ util/ windows/
Z shell умудрился придумать сокращение даже для глоба (/). Вы можете заменять это выражение на единственный слеш:
$ ls -d -F */
ChangeLog-branches/ doc/ po/ src/ util/ windows/
===== конец врезки =====
Глобы в командной строке могут при необходимости повторяться. Несколько дополнительных примеров приведено в листинге ниже:
1) $ ls -1 -a -F
./libs
ChangeLog
ChangeLog-branches/
Makefile
Makefile.in
alloca.c
ansi2knr.c
cmpt.c
cmpt.o
config.h
config.h.in
connect.c
connect.h
connect.o
convert.c
convert.h
convert.o
...
wget*
2) $ ls -a -F .*
./lib
3) $ ls -1 *.?
alloca.c
ansi2knr.c
cmpt.c
cmpt.o
config.h
connect.c
connect.h
connect.o
convert.c
convert.h
convert.o
...
4) $ ls -1 ????.?
cmpt.c
cmpt.o
5) $ ls [a-c]?*.*
alloca.c
ansi2knr.c
cmpt.c
cmpt.o
config.h
config.h.in
connect.c
connect.h
connect.o
convert.c
convert.h
convert.o
cookies.c
cookies.h
cookies.o
Первая команда показывает все файлы в директории, включая скрытые, т.е. те чьи имена начинаются с точки (опция -a). Список выводится в одну колонку (опция -1). Директории отмечаются слешем в конце имени, а исполнимые файлы - звёздочкой (опция -F).
Вторая команда ищет файлы, чьё имя начинается с точки (.*).
Третья команда находит файлы, имеющие однобуквенное расширение имени.
Четвёртая команда отыскивает файлы, чьё имя состоит из четырёх символов, точки и ещё одного символа.
И, наконец, пятая команда ищет файлы, чьё имя начинается на a, b или c, затем должен быть по крайней мере один символ (или больше), затем точка и любое расширение.
Как вы могли видеть, комбинации из нескольких глобов способны на многое.
А что произойдёт, если выполнить "ls *.z" (в предположении, что таких файлов в директории нет)? Вы увидите весьма полезное сообщение об ошибке:
$ ls *.z
zsh: no matches found: *.z
Урок истории (команд)
Итак, мы разобрались с путями и методами отбора файлов. Вы уже можете начать самовыражаться в командной строке. Однако, даже если все ваши команды станут очень короткими и красивыми, вас может утомить бесконечное повторение одних и тех же вещей. К счастью, большинство оболочек ведёт запись истории выполненных команд. Вам только надо отыскать вашу команду в списке и запустить её. Для работы с историей также введены удобные сокращения.
Работа с историей в Z shell настраивается так:
$ HISTSIZE=500
$ SAVEHIST=500
Эти переменные окружения определяют, что и сама оболочка и файл должны содержать не более 500 последних команд.
После того как вы некоторое время поработаете в оболочке, наберите history:
$ history
...
781 /bin/ls -d */
782 /bin/ls -F *(/)
783 /bin/ls -d -F *(/)
784 /bin/ls -d -F */
785 /bin/ls -d */
Каждой команде, которую вы ранее запускали, присвоен порядковый номер. Вы можете использовать этот номер для вызова и повторного выполнения конкретной команды. Для перезапуска команды достаточно ввести восклицательный знак и её номер без пробела:
$ !785
ChangeLog-branches/ doc/ po/ src/ util/ windows/
Если же вам понадобилась не вся команда, а только какой-либо из аргументов, можно добавить к предыдущей записи двоеточие, после которого указать номер аргумента. При этом 0 ссылается на имя команды, 1 - на первый аргумент и т.д. Ниже приведены несколько примеров работы с историей:
$ echo !782:2
echo *(/)
ChangeLog-branches doc po src util windows
$ ls AUTHORS COPYING INSTALL MACHINES
AUTHORS COPYING INSTALL MACHINES
$ echo !!:3
echo INSTALL
$ history -2
788 ls AUTHORS COPYING INSTALL MACHINES
789 echo INSTALL
$ echo !788^
echo AUTHORS
AUTHORS
$ echo !788$
echo MACHINES
MACHINES
Команда "history -2" печатает две последние выполненные команды. Символы каретки (^) и доллара ($) используются для ссылки на первый и последний аргументы команды соответственно. Можно сослаться сразу на некоторый диапазон аргументов, например как здесь:
$ echo AUTHORS COPYING INSTALL MACHINES
AUTHORS COPYING INSTALL MACHINES
$ echo !!:1-2
echo AUTHORS COPYING
AUTHORS COPYING
Есть ещё один, достаточно эффективный путь вызова команды из истории:
$ ls I*
$ ls M*
$ echo !?M
echo ls M*
Конструкция !?M производит поиск по истории и перевыполняет последнюю команду, в которой нашлась заглавная буква M.
Переменные окружения
Работа с командной строкой -- это существенный навык, но им понятие свободного владения языком UNIX не ограничивается. Вам придётся общаться с большим зоопарком разнообразных программ. В переменных окружения храняться настройки как собственно оболочки так разнообразных утилит. При этом вы можете распространить свои предпочтения на любую программу, запускаемую из оболочки.
Некоторые переменные окружения, такие как $SAVEHIST, оказывают влияние только на саму оболочку.
Другие, которые называются экспортируемыми, действуют глобально, т.е. копируются в пространство процесса (окружения) каждой программы, которую вы запускаете из оболочки. Например, в переменной $HOME записан путь к домашней директории текущего пользователя. Эта переменная устанавливается в процессе регистрации (login) пользователя в системе. Впоследствии, оболочка, будучи запущенной использует эту переменную для поиска своих файлов настроек. Другие приложения также обращаются к ней, SSH и FTP -- чтобы найти файл .netrc, в котором хранятся пароли для удалённог доступа. Такие переменные как $HOME, $PATH и $SHELL могут быть использованы всеми программами. Другие могут иметь смысл только для отдельных приложений.
Для ознакомления со всеми вашими переменными окружения напечатайте printenv. Вы должны увидеть что-то похожее на это:
$ printenv
PATH=/Users/strike/bin:/Applications/xampp/xamppfiles/bin:/Users/strike/bin:/usr/bin:/
bin:/usr/sbin:/sbin
HOME=/Users/strike
SHELL=/bin/zsh
USER=strike
TERM=xterm-color
LOGNAME=strike
SHLVL=1
PWD=/Local/src/versions/wget/wget-1.9
OLDPWD=/Local/src/versions/wget/wget-1.9/src
PERL5LIB=/Applications/xampp/xamppfiles/lib/perl5/site_perl/5.8.7:/Projects/IGSP/src
CLICOLOR=true
MANPATH=/Local/root/share/man:/usr/share/man:/opt/local/share/man
INFOPATH=/opt/local/share/info
LESS=-n
Скорее всего, вы узнаете большинство из переменных, но некоторые окажутся незнакомыми. $SHLVL (shell levev, уровень оболочки) показывает как глубоко вы погрузились, запуская одну оболочку из другой. 1 соответствует оболочке входа в систему. Вы можете использовать это значение, чтобы изменять вид приглашения командной строки для каждой новой вложенной оболочки. $TERM отражает тип вашего терминала, что важно для правильного отображения текста, цветов и реакции на различные комбинации клавиш. $PWD содержит имя текущей рабочей директории, а $OLDPWD -- имя предыдущей рабочей директории. Эти переменные можно использовать для быстрого перемещения по директориям как показано ниже:
$ echo $PWD
/Users/strike
$ echo $OLDPWD
/Local/src/versions/wget/wget-1.9
$ cd $OLDPWD
$ echo $PWD
/Local/src/versions/wget/wget-1.9
$ echo $OLDPWD
/Users/strike
Остальные переменные относятся к отдельным приложениям. $PERL5LIB содержит пути, где Perl должен искать дополнительные библиотеки. Команда ls использует переменную $CLICOLOR для раскрашивания файлов в соответствии с их типом и расширением. Такие переменные обычно документируются в руководствах, посвященных программам, которые их используют.
Переменные окружения определяются также как и переменные оболочки, но для того чтобы они стали доступны другим приложениям, их надо экспортировать:
$ MYVARIABLE=$HOME/projectX
$ export TMPDIR=/tmp/projectX
Первая из команд задаёт переменную $MYVARIABLE. При присвоении переменной значения знак доллара перед именем не ставится, но в дальнейшем при использовании переменной для получения значения, он обязателен. $MYVARIABLE видима только оболочке, поскольку мы её не экспортировали. Для вывода списка переменных оболочки используется команда set. Её вывод будет включать и переменные окружения, видимые внешним программам, поскольку оболочке они тоже видны.
Вторая команда определяет и экспортирует переменнную $TMPDIR. Одно из многих приложений, использующих эту переменную -- компилятор GCC. По пути, который мы записали в $TMPDIR, GCC будет создавать свои временные файлы.
Если вам потребуется удалить какую-либо из переменных окружения, воспользуйтесь командой unset:
$ set
HOME=/Users/strike
MYVARIABLE=/Users/strike/projectX
TMPDIR=/tmp/projectX
...
$ unset MYVARIABLE TMPDIR
$ set
HOME=/Users/strike
....
Псевдонимы и файлы запуска
Файлы запуска перечитываются оболочкой всякий раз, как она стартует. Это идеальное место для хранения всех настроек: пременных оболочки, пременных окружения и псевдонимов.
Псевдоним -- это достаточно короткая последовательность символов, используемая вами для представления более длинной и сложной команды. Вместо того, чтобы раз за разом набирать:
$ find /home/joe -type f -name '*.txt' -print | xargs grep -l "Monthly Report"
вы можете создать псевдоним и набирать:
$ findreports
Оболочка сделает за вас всю тяжелую работу, заменив короткую команду на длинную. Для создания псевдонима findreports, введите:
alias findreports='find $HOME -type f -name "*.txt" -print | xargs grep -l "Monthly Report"'
Одиночные кавычки должны выделять команду. Если такие используются внутри команды, заключите команду в двойные кавычки. Псевдонимы могут содержать другие элементы командной строки, такие как: переменные, операторы перенаправления, другие псевдонимы, метасимволы и т.п. Ниже приведены примеры работы псевдонимов:
$ alias ll='/bin/ls -l'
$ ll -d 2002*
drwxrwxr-x 2 www-data www-data 4096 Jan 16 2002 2002-02
drwxrwxr-x 2 www-data www-data 4096 Jan 22 2002 2002-03
drwxrwxr-x 2 www-data www-data 4096 Apr 15 2002 2002-04
drwxrwxr-x 2 www-data www-data 4096 Apr 19 2002 2002-05
...
$ alias lt='ll -t'
$ lt -d 2002*
drwxrwxr-x 2 www-data www-data 4096 Apr 19 2002 2002-05
drwxrwxr-x 2 www-data www-data 4096 Apr 15 2002 2002-04
drwxrwxr-x 2 www-data www-data 4096 Jan 22 2002 2002-03
drwxrwxr-x 2 www-data www-data 4096 Jan 16 2002 2002-02
$ alias m='pinky | grep mstreicher'
$ m
mstreicher Martin Streicher ...
$ alias snap='pinky >> ~/.pinky'
$ snap
$ snap
$ cat ~/.pinky
Login Name TTY Idle When Where
mstreicher Martin Streicher pts/0 Jun 18 16:40 cpe-071-065-224-025.nc.res.rr.com
Login Name TTY Idle When Where
mstreicher Martin Streicher pts/0 Jun 18 16:40 cpe-071-065-224-025.nc.res.rr.com
Псевдоним ll ссылается на /bin/ls, использование абсолютного пути предохраняет от возможных подстановок.
Когда вы ввели ll, это слово было заменено псевдонимом и остаток строки был добавлен к команде. Т.о. "ll -d 2002*" превратилась в "/bin/ls -l -d 2002*". Псевдоним lt ссылается на ll и добавляет к этой команде флаг -t для сортировки файлов по времени последнего изменения. Вызов lt превращается в "/bin/ls -l -t -d 2002*".
Псевдоним m использует пайп. Псевдоним snap использует двойную воронку для дописывания вывода команды к содержимому файла.
Чтобы посмотреть, какие псевдонимы уже определены в вашей оболочке, введите alias (без параметров):
$ alias
alias findreports='find $HOME -type f -name "*.txt" -print | xargs grep -l
"Monthly Report"'
alias ll='/bin/ls -l'
alias lt='ll -t'
alias m='pinky | grep mstreicher'
alias snap='pinky >> ~/.pinky'
...
Если требуется удалить некий псевдоним, используйте unalias. Причём, псевдонимы можно удалять списками:
$ unalias m snap
$ alias
alias findreports='find $HOME -type f -name "*.txt" -print | xargs grep -l
"Monthly Report"'
alias ll='/bin/ls -l'
alias lt='ll -t'
О'кей, вы славно потрудились, настраивая все эти переменные и псевдонимы, и теперь хотели бы сохранить результаты труда, так чтобы при следующем заходе в систему или при запуске второго терминала не пришлось бы всё заново настраивать.
Все настройки можно сохранять в файлах запуска, которые перечитываются оболочкой при каждом запуске. Эти файлы могут быть как достаточно простыми (переменная=значение), так и довольно сложными, включающими анализ ситуации на фьючерсных рынках и учитывающими расположение планет. Некоторые пользователи держат несколько комплектов инициализационных файлов под разные проекты.
Z shell читает настройки из .zshrc и .zprofile, в вашей домашней директории. Файл .zshrc обрабатывается каждый раз, когда оболочка стартует. А вот .zprofile считывается только в случае старта оболочки входа в систему (login shell).
После того как вы настроили оболочку, сохраните настройки:
$ set >> $HOME/.zshrc
$ alias >> $HOME/.zshrc
Возможно, вам захочется подредактировать полученный файл, удалив переменные специфичные для текущей сессии.