UNIX — страница 34 из 115

обобщаются и шаблоны, и действия. Команда
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]*
(буква, за которой следует нуль или более букв). Регулярное выражение
.*
соответствует —
*
, т.е. метасимволу интерпретатора, используемому для имен файлов.

Ни одно регулярное выражение команды

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

$ grep '^[^:]*::' /etc/passwd

Шаблон расшифровывается так: начало строки, любое число символов, отличных от двоеточия, два двоеточия.

Команда

grep
— старейшая в семействе программ, к которому относятся команды
fgrep
и
egrep
. В основном их действие одинаково, но