1. Операции с файлами и фильтрация

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

1.1. cat, tail, head, tee: Команды для вывода содержимого файлов

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

Утилита cat объединяет файлы и выводит результат на стандартный вывод, которым обычно является экран вашего компьютера. Это одна из наиболее часто используемых команд. Например, вы можете использовать:

# cat /var/log/mail/info

для вывода содержимого файла журнала почтового демона на стандартный вывод [47]. Команда cat имеет очень полезную опцию (-n), которая позволяет вам выводить номера строк.

Некоторые файлы, типа журналов демонов (если они запущены) обычно имеют довольно большой размер [48] и полный их вывод на экран будет не очень полезным. Вообще говоря, вам нужны только несколько строк из файла. Для этого вы можете воспользоваться командой tail. Следующая команда выведет (по умолчанию) последние 10 строк из файла /var/log/mail/info:

# tail /var/log/mail/info

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

# tail -f /var/log/mail/info

В этом случае все изменения в файле /var/log/mail/info будут немедленно выводиться на экран. Использование команды tail с опцией -f весьма полезно, когда вам нужно знать, как работает ваша система. Например, наблюдая за файлом журнала /var/log/messages, вы сможете всегда получать обновленную информацию о системных сообщениях и различных демонах.

Если вы используете tail для нескольких файлов, она будет выводить в отдельной строке имена этих файлов перед выводом их содержимого. Работа с опцией -f при этом также возможна, что является ценным дополнением для наблюдения за взаимодействием различных частей системы.

Вы можете использовать опцию -n для вывода последних n строк файла. Например, для вывода последних 2-х строк наберите:

# tail -n2 /var/log/mail/info

Как и для других команд, вы можете одновременно использовать разные опции. Например, при одновременном использовании опций -n2 и -f вы начнете с двух последних строк файла и будете продолжать наблюдать появление новых строк по мере добавления их в файл журнала.

Команда head похожа на tail, но она выводит первые строки файла. Следующая команда выведет (по умолчанию) первые 10 строк файла /var/log/mail/info:

# head /var/log/mail/info

Как и в случае с tail вы можете указать опцию -n для указания числа выводимых строк. Например, для вывода первых 2-х наберите:

# head -n2 /var/log/mail/info

Также вы можете использовать эти две команды совместно. Например, если вы хотите увидеть только строки 9 и 10, вы можете воспользоваться командой, в которой head выберет первые 10 строк из файла и передаст их по каналу в команду tail.

# head /var/log/mail/info | tail -n2

При этом последняя команда возьмет последние 2 строки и выведет их на экран. Таким же способом вы можете выбрать 20-ю от конца файла строку:

# tail -n20 /var/log/mail/info |head -n1

В этом примере мы говорим команде tail взять последние 20 строк и передать их по каналу в head. Затем команда head выводит на экран первую строку из полученных данных.

Допустим, что нам нужно вывести на экран результат последнего примера и сохранить его в файл results.txt. Нам может помочь утилита tee. Её синтаксис:

tee [опции] [файл]

Теперь мы можем изменить предыдущую команду следующим образом:

# tail -n20 /var/log/mail/info |head -n1|tee results.txt

Давайте рассмотрим еще один пример. Нам нужно выбрать последние 20 строк, сохранить их в файл results.txt, а на экран вывести только первую из них. Тогда мы должны ввести следующее:

# tail -n20 /var/log/mail/info |tee results.txt |head -n1

У команды tee есть полезная опция (-a), которая позволяет вам дописать данные в конец существующего файла.

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

1.2. grep: Поиск строк в файлах

Ни имя команды, ни её аббревиатура («General Regular Expression Parser» - синтаксический анализатор общих регулярных выражений) не являются интуитивными, однако её действие и использование довольно просты: grep ищет в одном или нескольких файлах шаблон, заданный в качестве аргумента. Её синтаксис:

grep [опции] <шаблон> [один или несколько файлов]

Если указано несколько файлов, в отображаемом результате их имена будут выводиться перед каждой найденной строкой. Вы можете использовать опцию -h для предотвращения вывода этих имен или вы можете использовать опцию -l для вывода только имен файлов с найденными совпадениями. Шаблон - это регулярное выражение, хотя в большинстве случаев он состоит из одного единственного слова. Наиболее часто используемые опции:

  • -i: поиск без учета регистра (т.е. игнорирование разницы между верхним и нижним регистром);

  • -v: обратный поиск. Вывод строк, которые не соответствуют шаблону;

  • -n: вывод номера строки для каждой из найденных строк;

  • -w: сообщает grep'у, что шаблон должен совпадать со всем словом.

Итак, давайте теперь вернёмся к анализу лог-файла почтового демона. Нам необходимо найти все строки в файле /var/log/mail/info, содержащие шаблон «postfix». Для этого мы вводим такую команду:

# grep postfix /var/log/mail/info

Если нам нужно найти все строки, НЕ содержащие шаблона postfix, нам необходимо воспользоваться опцией -v:

# grep -v postfix /var/log/mail/info

Команду grep можно использовать в каналах.

Допустим, что нам нужно найти все сообщения об успешно отправленных письмах. В этом случае мы должны отфильтровать все строки, добавленные почтовым демоном в файл журнала (содержащие шаблон postfix), и они должны содержать сообщение об успешной отправке (status=sent)[49]:

# grep postfix /var/log/mail/info |grep status=sent

В этом случае команда grep использована дважды. Это разрешается, но выглядит не совсем красиво. Тот же результат может быть получен при помощи утилиты fgrep. По сути fgrep - это более простой способ для вызова grep -F. Сначала нам нужно создать файл, содержащий шаблоны, записанные в отдельной строке каждый. Такой файл может быть создан следующим образом (мы используем patterns.txt в качестве имени файла):

# echo -e 'status=sent\npostfix' >./patterns.txt

Проверьте результат командой cat. \n - это специальный шаблон, означающий «новую строку».

Затем мы вызываем следующую команду, в которой мы используем файл patterns.txt и утилиту fgrep вместо «двойного вызова» команды grep:

# fgrep -f ./patterns.txt /var/log/mail/info

Файл ./patterns.txt может содержать сколько угодно шаблонов. Например, для выборки сообщений о письмах, успешно отправленных на адрес peter@mandriva.com, достаточно будет добавить этот электронный адрес в наш файл ./patterns.txt, выполнив следующую команду:

# echo 'peter@mandriva.com' >>./patterns.txt

Понятное дело, что вы можете комбинировать команду grep с tail и head. Если нам нужно найти сообщения о предпоследнем электронном письме, отправленном на адрес peter@mandriva.com, мы используем:

# fgrep -f ./patterns.txt /var/log/mail/info | tail -n2 | head -n1

Здесь мы применили описанный выше фильтр и отправили результат через канал в команды tail и head. Они выбрали из данных предпоследнее значение.

1.3. egrep: Регулярные выражения и фильтрование

С помощью grep мы ограничены шаблонами и фиксированными данными. Как нам найти все электронные письма, отправленные каждому работнику «ABC Company»? Перечисление всех их электронных адресов будет не такой уж и простой задачей, т.к. мы можем пропустить кого-либо, или нам придется вручную копаться в файле журнала.

Как и в случае с fgrep, grep имеет сокращенный вызов для команды grep -E: egrep. egrep использует регулярные выражения вместо шаблонов, предоставляя нам более мощный интерфейс для «grep'анья» текста.

Вдобавок к тому, что мы упоминали в разделе Раздел 3, «Шаблоны подстановки в командном процессоре» при рассмотрении шаблонов подстановки, вот еще несколько дополнительных регулярных выражений:

  • [:alnum:], [:alpha:] и [:digit:] могут быть использованы вместо определения классов самих символов и представляют, соответственно: все буквы плюс все цифры, все буквы (верхний и нижний регистры) и все цифры. У них есть дополнительное преимущество - они включают в себя международные символы и учитывают региональные настройки системы.

  • [:print:] представляет все символы, которые могут быть выведены на экран.

  • [:lower:] и [:upper:] представляют все буквы верхнего и нижнего регистров соответственно.

Существует много других доступных классов и вы можете просмотреть их в egrep(1). Перечисленные выше классы являются наиболее часто используемыми.

После регулярных выражений могут следовать один или несколько различных повторяющихся операторов:

?

Предшествующий элемент является необязательным, т.е. соответствует ни одному или одному вхождению, но не более одного.

*

Предшествующий элемент будет соответствовать 0 или более вхождениям.

+

Предшествующий элемент будет соответствовать одному или более вхождениям.

{n}

Предшествующий элемент соответствует ровно n вхождениям.

{n,}

Предшествующий элемент соответствует n или более вхождениям.

{n,m}

Предшествующий элемент соответствует как минимум n вхождениям, но не более m раз.

Если вы заключите регулярное выражение в квадратные скобки, позже вы сможете восстановить его. Допустим, что вы указали выражение [:alpha:]+. Оно может представлять слово. Если вы хотите определить дважды повторяющиеся слова, вы можете поместить это выражение в скобки и повторно использовать его с помощью \1, если это первая группа. У вас может быть до 9 таких «записей».

$ echo -e "abc def\nabc abc def\nabc1 abc1\nabcdef\nabcdabcd\nabcdef abcef" > testfile
$ egrep "([[:alpha:]]+) \1" testfile
abc abc def
$
[Замечание]Замечание

Символы [ и ] являются частью имени группы, поэтому мы должны включить их, чтобы использовать этот класс символов. Первый знак [ сообщает, что мы будем использовать группу символов, вторая скобка является частью имени группы, а затем следуют соответствующие закрывающие скобки ].

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

Также вы можете использовать символ |, определяющий вхождение для выражения слева от знака | или для выражения справа от этого знака. Этот оператор объединяет эти выражения. Используя созданный ранее файл testfile, вы можете попробовать поискать выражения, содержащие только дублирующиеся слова или содержащие дублирующиеся слова с числами:

$ egrep "([[:alpha:]]+) \1|([[:alpha:][:digit:]]+) \2" testfile
abc abc def
abc1 abc1
$

Обратите внимание, что для второй группы, использующей скобки, мы должны использовать \2, в противном случае выражение не будет соответствовать тому, что нам нужно. Более эффективным выражением в данном отдельно взятом случае будет:

$ egrep "([[:alnum:]]+) \1" testfile
abc abc def
abc1 abc1
$

И, в заключение, для использования опредёленных символов вы должны их «заэкранировать», вставив перед ними обратную косую черту. Вот эти символы: ?, +, {, |, (, ) и конечно же \. Для использования их в своих выражениях вы должны писать: \?, \+, \{, \|, \( \) и \\.

Эта маленькая хитрость может помочь избежать повторения вводимых слов в «вашем вашем» тексте.

Регулярные выражения во всех утилитах должны соблюдать эти (или очень похожие) правила. Потратив некоторое время на их понимание, вы во многом поможете себе при работе с другими утилитами, такими как sed. sed - это утилита, которая среди всего прочего может обрабатывать текст путем его изменения с использованием регулярных выражений в качестве правил.

1.4. wc: Подсчет элементов в файлах

Команда wc (Word Count - подсчёт слов) используется для подсчёта числа строк, слов и символов в файлах. Также она полезна для вычисления самой длинной строки. Её синтаксис:

wc [опции] [файл(ы)]

Список полезных опций:

  • -l: вывод количества строк;

  • -w: вывод количества слов;

  • -m: вывод общего количества символов;

  • -c: вывод количества байт;

  • -L: вывод длины самой длинной строки в тексте.

По умолчанию команда wc выводит количество строк, слов и символов. Вот несколько примеров использования:

Если нам нужно определить число пользователей в нашей системе, мы можем ввести:

$ wc -l /etc/passwd 

Если нам нужно узнать число CPU в нашей системе, мы пишем:

$ grep "model name" /proc/cpuinfo |wc -l

В предыдущем разделе мы получили список сообщений об успешно отправленных письмах на адреса, перечисленные в нашем файле ./patterns.txt. Если нам нужно узнать количество этих сообщений, мы можем перенаправить наш отфильтрованный результат через канал в команду wc:

# fgrep -f ./patterns.txt /var/log/mail/info | wc -l

1.5. sort: Сортировка содержимого файла

Ниже представлен синтаксис этой мощной утилиты для сортировки[50]:

sort [опции] [файл(ы)]

Давайте отсортируем часть файла /etc/passwd. Как видите, этот файл не отсортирован:

$ cat /etc/passwd

Если нам нужно отсортировать его по полю login, мы набираем:

$ sort /etc/passwd

По умолчанию команда sort сортирует информацию по первому полю в порядке возрастания (в нашем случае по полю login). Чтобы отсортировать данные в порядке убывания, используйте опцию -r:

$ sort -r /etc/passwd

Для каждого пользователя имеется свой собственный UID, записанный в файле /etc/passwd. Следующая команда сортирует файл в порядке возрастания по полю UID:

$ sort /etc/passwd -t":" -k3 -n

Здесь мы используем следующие опции sort:

  • -t":": сообщает sort'у, что разделителем полей является символ ":";

  • -k3: означает, что сортировка должна быть выполнена по третьему столбцу;

  • -n: сообщает что выполняется сортировка числовых данных, а не буквенных.

То же самое может быть выполнено в обратном порядке:

$ sort /etc/passwd -t":" -k3 -n -r

Обратите внимание, что sort обладает двумя важными опциями:

  • -u: строгая сортировка: исключаются повторяющиеся поля сортировки;

  • -f: игнорирование регистра (строчные символы обрабатываются так же, как и прописные).

И, наконец, если мы хотим найти пользователя с максимальным UID, мы можем воспользоваться следующей командой:

$ sort /etc/passwd -t":" -k3 -n |tail -n1

где мы сортируем файл /etc/passwd в порядке возрастания по столбцу UID и перенаправляем результат по каналу в команду tail. Далее выводится первое значение отсортированного списка.



[47] Некоторые примеры в этом разделе основаны на реальной работе с файлами журналов некоторых серверов (служб, демонов). Убедитесь, что у вас запущен syslogd (позволяет журналировать действия демонов) и соответствующий демон (в нашем случае Postfix), и что вы в работаете под root'ом. Естественно вы всегда можете применять наши примеры к другим файлам.

[48] Например, файл /var/log/mail/info содержит информацию обо всех отправленных письмах, сообщениях о выборке почты пользователями по протоколу POP и т.п.

[49] Хотя можно выполнить фильтрацию просто по шаблону состояния, пожалуйста, позвольте нам показать вам в этом примере новую команду.

[50] Здесь мы только вкратце рассмотрим sort. Об ее возможностях можно написать целую книгу.