Основы работы командной оболочки *NIX

Прежде чем обсуждать написание сценариев оболочки, рассмотрим некоторые базовые команды и синтаксис языка командной оболочки. Материал этого раздела актуален для всех основных оболочек sh-семейства (включающего оболочки bash и ksh, но не csh или tcsh), вне зависимости от используемой вами платформы. Опробуйте формы, с которыми вы пока еще не знакомы, и не бойтесь экспериментировать!

Редактирование команд

Мы наблюдали, как многие редактируют командные строки с помощью клавиш управления курсором. Вы ведь так не поступаете в своем текстовом редакторе, верно?

Все основные команды редактора emacs доступны при редактировании уже выполненного ряда команд (т.е. "предыстории"). Нажав комбинацию клавиш <Ctrl+E>, вы переместитесь в конец строки, а с помощью комбинации клавиш <Ctrt+A> — в начало. Нажатие комбинации клавиш <Ctrl+P> позволяет вернуться к предыдущим командам и вызвать их для редактирования. С помощью комбинации клавиш <Ctrl+R> вы сможете шаг за шагом "прокрутить" свою "предысторию" и найти старые команды.

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

$   set  -о vi

В режиме vi редактирование является модальным, но вы начинаете работать в режиме ввода команд. Для того чтобы выйти из режима ввода, нажмите вначале клавишу <Esc>, а затем клавишу <i>, чтобы вернуться в него. В режиме редактирования нажатие клавиши <w> переместит вас вперед на одно слово; использование комбинаций клавиш <f+X> позволит найти следующий символ "X" в строке и т.д. "Прогуляться" по командам, зафиксированным в "предыстории", можно с помощью клавиш <Esc> и <к>. Решили вернуться в режим emacs-редактирования? Выполните эту команду.

$ set -о emacs

Каналы и перенаправление потоков

Каждому процессу доступны по меньшей мере три информационных канала: "стандартный ввод" (STDIN), "стандартный вывод" (STDOUT) и "стандартная ошибка" (STDERR). Эти каналы устанавливаются ядром системы "от имени процесса", и поэтому сам процесс не обязательно знает их направленность. Они, например, могут быть связаны с окном терминала, файлом, подключением к сети или с каналом, принадлежащим к другому процессу.

Для систем UNIX используется унифицированная модель ввода-вывода, в которой каждому каналу присваивается целое число, именуемое файловым дескриптором. Точное число (номер), назначаемое каналу, обычно не имеет значения, но каналам STDIN, STDOUT и STDERR гарантированно соответствуют файловые дескрипторы 0, 1 и 2 соответственно, чтобы обеспечить безопасное обращение к ним по номерам. В контексте интерактивного окна терминала канал STDIN обычно считывает данные с клавиатуры, а оба канала STDOUT и STDERR записывают свои выходные данные на экран.

Большинство команд принимает входные данные из канала STDIN. Выходная информация записывается ими в канал STDOUT, а сообщения об ошибках — в канал STDERR. Это соглашение позволяет объединять команды подобно строительным блокам для организации конвейерной обработки данных.

Командная оболочка интерпретирует символы "<", ">" и "»" как инструкции по изменению направления передаваемых командой данных в файл или принимаемых данных из файла. Символ "<" связывает канал STDIN с содержимым некоторого существующего файла. Символы ">" и ">>" перенаправляют поток STDOUT; причем символ ">" используется для замены содержимого файла, а "»" — для добавления данных в его конец. Например, команда

$ echo "Это тестовое сообщение." > /tmp/mymessage

сохраняет одну строку в файле /tmp/mymessage (при необходимости файл будет создан). Следующая команда отправляет содержимое этого файла по электронной почте пользователю j ohndoe.

$ mail -s "Mail test" johndoe < /tmp/mymessage

Для того чтобы перенаправить потоки STDOUT и STDERR в одно и то же место, используйте символ ">&". Для того чтобы перенаправить только поток STDERR, используйте вариант "2>".

На примере команды find можно показать, почему важно обрабатывать потоки STDOUT и STDERR отдельно. Дело в том, что она формирует выходные данные по обоим каналам, особенно в случае ее выполнения непривилегированным пользователем. Например, при выполнении команды

$ find / -name core

обычно генерируется так много сообщений об ошибках "permission denied" (отсутствие прав доступа), что настоящие результаты поиска теряются в "шуме". Чтобы отбросить все сообщения об ошибках, можно использовать такой вариант. $ find / -name core 2> /dev/null

В этой версии команды find только настоящие совпадения (где пользователь имеет разрешение на чтение родительского каталога) выводятся в окно терминала. Чтобы сохранить список совпадающих путей в файле, выполните такую команду.

$ find / -name core > /tmp/corefiles 2> /dev/null

Эта командная строка отправляет совпадающие пути в файл /tmp/corefiles, отбрасывая все ошибки и ничего не посылая в окно терминала.

Для того чтобы связать канал STDOUT одной команды с каналом STDIN другой, используйте символ " |". Вот два примера.

$ ps -ef | grep httpd

$ cut -d:  -f7 < /etc/passwd | sort -u

В первом примере выполняется команда ps, генерирующая список процессов, из которого при "проходе" через команду grep выбираются строки, содержащие слово "httpd". Результат выполнения команды grep никуда больше не перенаправляется, поэтому совпадающие строки попадают в окно терминала.

Во втором примере команда cut выбирает из файла /etc/passwd пути к оболочке каждого пользователя. Список оболочек затем отправляется через команду sort -и, чтобы сформировать отсортированный список уникальных значений.

Для того чтобы последующая команда выполнялась только в случае успешного выполнения предыдущей, можно разделить эти команды символом "&&". Например, командная строка

$ lpr /tmp/t2 && rm /tmp/t2

удалит файл /tmp/t2 тогда и только тогда, когда она успешно дождется очереди на печать. В данном случае успех выполнения команды lpr будет зафиксирован при получении кода завершения, равного нулю. Использование для этой цели символа означающего операцию "логическое И", может сбить с толку тех, кто в других языках программирования использовал вычисления в сокращенной форме записи. Если кому то это непонятно, не стоит слишком долго здесь застревать; а просто примите это как идиому командной оболочки.

И наоборот, символ " | | " обеспечит выполнение следующей команды только в том случае, если предыдущая команда не выполнится (т.е. сгенерирует ненулевое значение кода завершения).

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

ср —preserve —recursive /etc/* /spare/backup \

I I echo "Did NOT make backup"

Для получения обратного эффекта, т.е. объединения нескольких команд в одной строке, можно использовать в качестве разделителя точку с запятой.

 

Использование переменных и кавычек

В операциях присваивания имена переменных никак не маркируются, но предваряются знаком доллара при ссылке на их значения.

$ etcdir='/etc'

$ echo $etcdir

/etc

Не ставьте до и после знака равенства (=) пробелы, в противном случае оболочка ошибочно примет имя вашей переменной за имя команды.

При ссылке на переменную можно заключить ее имя в фигурные скобки, чтобы синтаксический анализатор (да и сам человек) не сомневался в том, где заканчивается имя переменной и начинается другой текст; например, используйте запись ${etcdir} вместо $etcdir. Обычно без фигурных скобок можно обойтись, но они могут оказаться полезными в случае, если вам понадобится раскрывать переменные в строках с двойными кавычками. Часто нужно сделать так, чтобы после значения переменной стояли буквы или знаки препинания.

$   echo "Saved ${rev}th version of mdadm.conf."

Saved 8th version of mdadm.conf.

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

Переменные среды автоматически импортируются в пространство имен оболочки bash, поэтому их можно устанавливать и считывать с использованием стандартного синтаксиса. Для того чтобы перевести переменную оболочки в ранг переменной среды, используйте команду export имя переменной. Команды для переменных среды, которые вы хотите установить во время регистрации (при входе в систему), должны быть включены в ваш файл ~/ .prof ile или ~/.bash_profile. Другие переменные среды, такие как PWD (для хранения имени текущего рабочего каталога), автоматически создаются командной оболочкой.

Командная оболочка интерпретирует строки, заключенные в одинарные и двойные кавычки, почти одинаково. Различие состоит в том, что строки в двойных кавычках служат субъектами для универсализации файловых имен (замены реальных символов в имени и расширении файла универсальными, т.е. такими метасимволами, как "*" и "?") и для раскрытия переменных (замены переменных их значениями).

$ mylang="Pennsylvania Dutch"

$ echo "I speak ${mylang}."

I speak Pennsylvania Dutch.

$   echo 'I speak ${mylang}.'

I speak ${mylang}.

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

$   echo "There are 'wc -1 /etc/passwd4 lines in the passwd file."

There are 28 lines in the passwd file.

 

Команды общих фильтров

Любые корректные команды, которые считывают входные данные из канала STDIN и записывают результаты в канал STDOUT, могут быть использованы в качестве фильтра (т.е. компонента конвейера) для обработки данных. В этом разделе мы кратко рассмотрим некоторые из часто используемых команд фильтрации (хотя их полный список практически бесконечен). Команды фильтрации отличаются настолько сильным "коллективизмом", что порой даже трудно продемонстрировать их индивидуальное использование.

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

Команда cut: разбивка строк на поля

Команда cut выводит выбранные части входных строк. Чаще всего эта команда используется для извлечения полей (ограниченных некоторыми разделителями), как в приведенном выше примере, который для удобства читателей повторим еще раз.

$   cut  -d:   -f7  <   /etc/passwd   |   sort  -u

Команда cut также может возвращать сегменты, определенные границами столбцов текста. В качестве стандартного разделителя служит символ <ТаЬ>, но с помощью ключа -d разделители можно изменить. Ключ -f позволяет указать, какие поля нужно включить в результат.

Команда sort: сортировка строк

Команда sort сортирует входные строки. Звучит просто, да? Хотя на самом деле не все так безоблачно: существуют потенциальные нюансы, связанные с точно заданными частями сортируемых строк (т.е. с использованием сортировочного ключа) и порядком сортировки. Наиболее распространенные варианты применения этой команды показаны в табл. 2.1, в отношении же остальных случаев обратитесь к соответствующей man-странице.

На примерах приведенных ниже команд демонстрируется различие между числовой и лексикографической сортировкой (последняя действует по умолчанию). В обеих командах используются ключи -t: и -кЗ, 3 для сортировки файла /etc/group по его третьему полю (в качестве разделителя используется двоеточие), содержащему идентификатор (ID) группы. Первая команда реализует числовую сортировку, а вторая — лексикографическую (в алфавитном порядке).

$ sort -t:  -к3,3 -n /etc/group1

root:x:0:

bin:x:1rdaemon daemon:x:2:

$ sort -t: -k3,3 /etc/group

root:x:0:

bin:x:1:daemon

users:x:100:

 

Команда uniq: вывод уникальных строк

Команда uniq по существу подобна команде sort -и, но у нее есть некоторые полезные ключи, которые команда sort не эмулирует. Так, ключ -с используется для подсчета количества экземпляров каждой строки, ключ -d — для отображения только строк, имеющих дубликаты, а ключ -и — для отображения только строк, не имеющих дубликаты. При этом входные данные должны быть предварительно отсортированы, обычно путем "пропускания" через команду sort.

Например, по результатам выполнения следующей команды видно, что у 20 пользователей поле login shell (стартовая оболочка) содержит значение /bin/bash и у 12 — /bin/false. (Последний результат характерен либо для псевдопользователей, либо для пользователей, учетные записи которых были заблокированы.)

 

$ cut -d: -f7 /etc/passwd | sort | uniq -с

20 /bin/bash

12 /bin/false

 

Команда wc: подсчет строк, слов и символов

Подсчет количества строк, слов и символов в файле — еще одна распространенная операция, удобным средством реализации которой и является команда wc (word count). Выполненная без каких-либо ключей, она выведет все три результата подсчета.

$ wc /etc/passwd

32    77 2003 /etc/passwd

В контексте написания сценариев команду wc часто применяют только с одним из ключей (-1, -w или -с), чтобы результат ее выполнения состоял из одного числа. Эту форму чаще всего можно встретить внутри обратных апострофов, и тогда результат может быть сохранен или использован по назначению.

 

Команда tee: копирование входного потока в два места

Командный конвейер, как правило, действует прямолинейно, но зачастую полезно распараллелить поток данных, т.е. "перехватить" его и отправить копию в файл или окно терминала. Это можно сделать с помощью команды tee, которая отправляет свой стандартный входной поток как в стандартный выходной канал, так и в файл, указанный в командной строке. Представьте действие этой команды в виде тройника в трубопроводе (перев. с англ. tee — тройник).Устройство /dev/tty можно считать синонимом для текущего терминала. Например, команда

 

$ find / -name core | tee /dev/tty | wc -1

выводит найденные путевые имена файлов core и результат подсчета их количества.

Часто работа конвейера с командой tee, выводящей результаты и в файл, и в окно терминала (для проверки), занимает много времени. Вы можете понаблюдать за первыми результатами, чтобы убедиться в том, что все работает как надо, а затем смело уходите: ведь результаты же сохранятся в файле.

 

Команды head и tail: чтение файла с начала или с конца

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

Для интерактивного использования команда head уже несколько устарела, и вместо нее (в этом контексте) часто используется команда less, которая разбивает файлы для отображения по страницам. Но команду head можно успешно применять в сценариях и с другой целью. С командой tail используется ключ -f, который чрезвычайно полезен для системных администраторов. Вместо немедленного завершения (после вывода требуемого количества строк) команда tail -f ожидает "прибытия" новых строк, которые (как только они появятся "на горизонте") будут добавлены в конец файла, а затем выведены по назначению, что очень удобно для мониторинга журналов регистрации. Однако следует иметь в виду, что программа, которая реализует запись данных в файл, может буферизировать свои выходные данные. Даже если строки будут добавляться регулярно (с точки зрения логики), они могут стать видимыми только фрагментами объемом 1 или 4 Кбайт.

Для остановки процесса мониторинга нажмите комбинацию клавиш <Ctrl+C>.

 

Команда grep: поиск текста

При выполнении команды grep по мере просмотра входного текста выводятся строки, которые совпадают с заданным образцом (шаблоном). Свое название эта команда получила "в наследство" от команды д/regular_expression/p) из старого (и ныне действующего) редактора ed, используемого в самых первых версиях UNIX.

"Регулярные выражения" (regular expressions) — это шаблоны предназначенные для поиска совпадающего с ними текста и написанные на языке шаблонов. Они представляют собой универсальный стандарт, используемый большинством программ при поиске по совпадению с шаблоном, хотя и с небольшими различиями в реализациях. Странное имя команды уходит своими корнями в оригинальные изыскания соответствующих разделов теории вычислений. Более детально синтаксис регулярных выражений рассматривается в разделе 2.3.

Подобно большинству фильтров, команда grep имеет множество ключей. Например, ключ -с позволяет выводить количество совпавших строк, ключ -i служит для игнорирования регистра букв при сопоставлении, а ключ -v предназначен для вывода отличающихся (а не совпавших) строк. Очень полезным является ключ -1 (прописная бук-Ва "L"), который заставляет команду grep выводить только имена файлов, содержащие совпавшие с шаблоном строки, а не все совпавшие строки.

Например, выполнение команды

$ sudo grep -1 mdadm /var/log/*

/var/log/auth.log /var/log/syslog.0

 

демонстрирует, что записи с именем утилиты mdadm нашлись в двух различных файлах системных журналов.

Команду grep можно считать классической реализацией базового механизма использования регулярных выражений, но в некоторых версиях предлагается выбор других диалектов. Например, Linux-команда grep -р служит для поиска выражений в стиле языка Perl, хотя в справочных страницах можно найти неопределенное предупреждение о том, что такой вариант носит "экспериментальный характер". Для полной уверенности в своих сценариях просто используйте язык Perl или Python.

 

Комментарии (0)

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.