Конец главы посвящен двум преобразователям данных общего назначения, или программируемым фильтрам. Они называются так потому, что конкретное преобразование записывается как программа на некотором простом языке программирования. Различные программы могут породить совершенно разные преобразования. Речь идет здесь о программах
sed
("stream editor" — потоковый редактор) и
awk
, имя которой составлено из начальных букв имен ее авторов. Обе программы получаются путем обобщения команды
grep
:
$ программа шаблон-действие имена_файлов...
которая сканирует последовательность файлов, ведя поиск строк, совпадающих с шаблоном, — если строка найдена, выполняется необходимое действие. Для команды
grep
, как и для редактора
ed
, шаблоном является регулярное выражение, а действие по умолчанию сводится к печати каждой строки,
соответствующей шаблону.
В программах
sed
и
awk
обобщаются и шаблоны, и действия. Команда
sed
, производная от
ed
, выполняет "программу", состоящую из команд редактирования. Она пропускает данные из файлов через эту программу, выполняя для каждой строки команды из программы. Команда
awk
не так удобна, как
sed,
для манипуляций с текстом, но в ней предусмотрены арифметические операции, переменные, встроенные функции и язык программирования, схожий с Си. В данной главе не приводится полное описание обеих программ; оно есть в т. 2B справочного руководства по UNIX.
4.1 Семейство программ
grep
В гл. 1 мы кратко упомянули о команде
grep
, а затем использовали ее в примерах. Конструкция
$ grep шаблон имена_файлов
проводит поиск в поименованных файлах или в стандартном входном потоке и выводит на печать каждую строку, в которую входит шаблон. Команда
grep
неоценима для поиска переменных в программах и слов в документах, а также для выбора части выходного потока программы:
$ grep -n variable *.[гл]
Поиск variable в тексте на Си
$ grep From $MAIL
Печать заголовков сообщений из почтовой
посылки
$ grep From $MAIL | grep -v mary
Заголовки, которые получены не от
адресата mary
$ grep -y mary $HOME/lib/phone-book
Поиск номера mary
$ who | grep mary
Выяснить, работает ли mary в системе
$ ls | grep -v temp
Имена файлов, не содержащих temp
Флаг
– n
инициирует вывод номеров строк, флаг
– v
меняет на противоположное значение условия, а флаг
– y
допускает сопоставление строчных букв из шаблона с прописными буквами из файла (но прописные буквы все-таки могут сопоставляться только с прописными).
Во всех рассматривавшихся до сих пор примерах проводился поиск обычных строк из букв и чисел. Но команда
grep
может искать и более сложные шаблоны: она интерпретирует выражения согласно простому языку для описания строк. С технической точки зрения шаблон представляет в некоторой степени ограниченную форму спецификаций строк, называемую регулярным выражением. Команда интерпретирует такие же регулярные выражения, как и редактор
ed
. На самом деле, эта команда была создана (за один вечер) прямым редактированием
ed
.
Регулярные выражения характеризуются тем, что ряду символов, таким, как
*
и т.п., приписывается специальное значение, используемое интерпретатором. Есть еще несколько метасимволов, но, к сожалению, с различными значениями. В табл. 4.1 показаны все метасимволы регулярных выражений, и мы кратко их здесь рассмотрим.
с
Любой неспециальный символ c соответствует самому себе
\c
Указание убрать любое специальное значение символа
c
^
Начало строки
$
Конец строки
.
Любой одиночный символ
[...]
Любой символ из
...;
допустимы диапазоны типа
a-z
[^...]
Любой символ не из
...
; допустимы диапазоны
\n
Строка, соответствующая n-му выражению
\(...\)
(только для
grep
)
r*
Нуль или более вхождений
r
r+
Одно
или более вхождений
r
(только для egrep)
r?
Нуль или одно вхождение
r
(только для egrep)
r1r2
За
r1
следует
r2
r1|r2
r1
или
r2
(только для egrep)
\(r\)
Помеченное регулярное выражение
r
(только для
grep
); может быть вложенным
(r)
Регулярное выражение
r
(только
для
grep); может быть вложенным
Никакое регулярное выражение не соответствует концу строки
Таблица 4.1: Регулярные выражения
grep
и
egrep
(в порядке убывания приоритета)
Метасимволы
^
и
$
привязывают шаблон к началу (
^
) или концу (
$
) строки. Например,
$ grep From $MAIL
ищет строки, содержащие
From
в вашей почтовой посылке, но
$ grep '^From' $MAIL
выдает строки, начинающиеся с
From
, которые, вероятнее всего, будут заглавными строками сообщений. Метасимволы регулярных выражений пересекаются с метасимволами интерпретатора, поэтому всегда имеет смысл заключать шаблоны команды
grep
в апострофы.
Команда
grep
допускает классы символов, подобные тем, что используются интерпретатором: так,
[a-z]
задает любую строчную букву. Но есть и различия — если класс символов команды
grep
начинается с символа слабого ударения то шаблон задает любой символ, кроме входящих в данный класс. Значит,
[^0-9]
задает любой символ, кроме цифры. Как и в интерпретаторе, обратная дробная черта экранирует символы
]
и
–
в классе символов, но команды
grep
и
ed
требуют, чтобы эти символы использовались там, где их значение недвусмысленно. Например, шаблон
[][-]
задает открывающую или закрывающую квадратную скобку либо знак минус.
Точка
'.'
эквивалентна
'?'
в интерпретаторе: она задает любой символ. (Точка, по всей видимости, есть символ, назначение которого различно для разных программ.) Ниже приводятся два примера:
$ ls -l | grep '^d'
Список имен вложенных каталогов
$ ls -l | grep '^.......rw'
Список файлов, доступных всем для чтения и записи
Символ
'^'
и семь точек задают любые семь символов в начале строки; в случае применения к выходному потоку команды
ls -l
задается любая строка права доступа.
Операция "повторитель" (
'*'
) применима в выражении к предваряющему ее символу или метасимволу (включая класс символов), и вместе они обозначают любое число вхождений символа или метасимвола. Например,
x*
задает последовательность букв
x
произвольной длины,
[a-zA-Z]*
— любую строку букв,
.*
— все до конца строки, а
.*x
— все до последнего символа
x
в строке включительно. Необходимо отметить несколько важных моментов, связанных с повторителем. Во-первых, повторитель действует только на один символ, поэтому
xy*
соответствует
x
, за которым идут
yy...
, но не последовательности типа
xyxyxy
. Во-вторых, любое число включает нуль, поэтому если вы хотите, чтобы символ присутствовал, в шаблоне его нужно повторить. Например, правильным выражением, задающим строку букв, является такое:
[a-zA-Z][a-zA-Z]*
(буква, за которой следует нуль или более букв). Регулярное выражение
.*
соответствует —
*
, т.е. метасимволу интерпретатора, используемому для имен файлов.