Книга: UNIX — универсальная среда программирования
4.1 Семейство программ grep
Разделы на этой странице:
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
. В основном их действие одинаково, но fgrep
может одновременно искать несколько литеральных строк, тогда как egrep
интерпретирует настоящие регулярные выражения, подобно grep
, но с использованием операций "or" и скобок для группировки выражений, что будет объяснено ниже.
Обе команды, fgrep
и egrep
, имеют флаг -f
для указания файла, из которого читается шаблон. В этом файле символы перевода строк разделяют шаблоны при параллельном поиске. Допустим, что некоторые слова вы пишете неправильно. В этом случае можно проверить документацию на наличие таких слов, поместив их в файл по одному на строке и воспользовавшись командой fgrep
:
$ fgrep -f типичные_ошибки документ
Регулярные выражения, интерпретируемые egrep
(они также приведены в табл. 4.1), — те же самые, что и в grep, но с небольшими добавлениями. Можно использовать скобки для группировки, поэтому (xy)*
задает пустую строку или любую последовательность xy
, xyxy
, xyxyxy
и т.д. Вертикальная черта |
является операцией or (или); today|tomorrow
соответствует today
или tomorrow
, как и to(day|morrow)
. Наконец, в команде egrep
есть еще две операции повторения: +
и ?
. Шаблон x+
задает один или более символов x
, а шаблон x?
— нуль или один символ x
(но не более).
Команда egrep
прекрасно подходит для игр, в которых нужно искать в словаре слова со специальными свойствами. Мы будем обращаться к словарю Вебстера (второе международное издание), хранящемуся в файле в виде списка слов по одному в строке без определений их значения. В вашей системе может быть небольшой словарь /usr/dict/words
, предназначенный для проверки правописания; просмотрите его, чтобы выяснить формат. Ниже приведен шаблон, задающий слова английского языка, содержащие все пять гласных в алфавитном порядке:
$ cat alphvowels
^[^aeiou]*a[^aeiou]*e[^aeiou]*i[^aeiou]*o[^aeiou]*u[^aeiou]*$
$ egrep -f alphvowels /usr/dict/web2 | 3
abstemious abstemiously abstentions
achelious acheirous acleistous
affectious annelidous arsenious
arterious bacterious caesious
facetious facetiously fracedinous
majestious
$
В файле alphvowels
шаблон не взят в кавычки. Если применяются кавычки для экранирования шаблона в команде egrep
, интерпретатор защищает его от интерпретации командами, но кавычки убирает, и команда egrep
никогда "не узнает" о них. Поскольку интерпретатор не заглядывает в файл, кавычки не нужны для защиты содержимого файла. Для этого примера мы могли бы использовать команду grep
, но алгоритм egrep
таков, что она осуществляет поиск намного быстрее в случае шаблонов с повторителями, особенно при просмотре больших файлов.
В другом примере требуется найти все английские слова, состоящие из шести или более букв, в которых буквы следуют в алфавитном порядке:
$ cat monotonic
^a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?r?s?t?u?v?w?x?y?z?$
$ egrep -f monotonic /usr/dict/web2 | grep '......' | 5
abdest acfcnow adipsy agnosy almost
bedfist behint befcnow bijoux biopsy
chintz dehors dehort demos dimpsy
egilops ghosty
(Egilops — это болезнь, поражающая пшеницу.) Обратите внимание на использование команды grep
для фильтрации выходного потока egrep
.
Для чего нужны три сходные программы? Программа fgrep
не интерпретирует метасимволы, но может параллельно обрабатывать тысячи слов (после инициации время ее работы не зависит от числа слов), и поэтому она применяется прежде всего для заданий типа библиографического поиска. Размеры типичных шаблонов для программы fgrep
превосходят возможности алгоритмов, используемых в программах grep
и egrep
. Различия между двумя последними указать труднее. Программа egrep
появилась намного раньше. Она интерпретирует регулярные выражения, используемые в командах редактора ed
, в ней есть помеченные регулярные выражения и большой набор флагов. Программа egrep
интерпретирует более общие выражения (не считая помеченных), и выполняется значительно быстрее (со скоростью, не зависящей от шаблона), но ее стандартная версия требует большего времени на инициацию в случае сложного выражения. Существует новая версия, начинающая работу мгновенно, так что программы egrep
и grep
теперь можно было бы скомбинировать в одну программу поиска по шаблону.
Упражнение 4.1
Прочтите о регулярных выражениях (( и )) в приложении 1 или справочном руководстве по ed(1)
. Используйте программу grep
для поиска палиндромов — слов, читающихся одинаково с конца и начала. Подсказка: составьте свой шаблон для слов каждой длины.
Упражнение 4.2
Алгоритм программы grep
таков: прочесть одну строку, проверить ее на вхождение шаблона, затем продолжить цикл. Как повлияло бы на работу программы то, что регулярные выражения могли бы задавать перевод строки?
- 8.2. Языки программирования Виды программирований
- 1.1. Введение в объектно-ориентированное программирование
- 11.2. СВОЙСТВА ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ
- СТРУКТУРА ПРОСТОЙ ПРОГРАММЫ
- ПРИМЕР ПРОСТОЙ ПРОГРАММЫ НА ЯЗЫКЕ СИ
- 6.2. Типичные ошибки при проведении программ продвижения и варианты их устранения
- Системное программное обеспечение
- Язык программирования Python
- 1.2.5. Пример программы
- 24.7. Использование программы-твикера
- Программирование на языке Пролог для искусственного интеллекта
- Программа «Тайный покупатель»