Опубликован: 14.12.2004 | Уровень: для всех | Доступ: платный | ВУЗ: Компания ALT Linux
Лекция 14:

Регулярные выражения

< Лекция 13 || Лекция 14: 12345 || Лекция 15 >

Поиск

Поиском подстрок в строках текста занимаются утилиты из семейства *grep - fgrep, grep и egrep. Первая из них - fgrep ( f ast grep) - не использует регулярные выражения, она просто читает текст со стандартного ввода или из файла и выдает на стандартный вывод строки, содержащие заданный в командной строке шаблон-подстроку. Утилита grep интерпретирует свой первый параметр как базовое регулярное выражение и выдает строки, которые содержат подстроку, соответствующую этому РВ. Утилита egrep работает с расширенными РВ, а в остальном аналогична grep. Об имени ( фамилии?) самого семейства - grep - рассказано в лекции 15.

Все утилиты семейства *grep распознают ключ -i ( i gnore case), при котором строчные и прописные буквы не различаются, ключ -v (in v ert), при котором выдаются строки, не содержащие шаблона, ключ -l, при котором выдаются только номера подходящих строк, и ключ -q ( q uiet, в cтарых версиях grep - -s, s ilent), при котором вообще ничего не выводится, а команда используется ради кода ошибки, равного нулю, только когда шаблон найден. Кроме того, если в командной строке *grep всего один параметр, который не является ключом, этот параметр распознается как шаблон, а текст считывается со стандартного ввода; если же таких параметров несколько, то второй и последующие распознаются как имена файлов, и текст считывается из них. Наконец, если параметр-шаблон состоит из нескольких строк (закавычивание позволяет так сделать), считается, что каждая из них содержит отдельное выражение для поиска, и подходящей будет считаться строка входного текста, включающая любое из выражений.

Еще раз повторим, что задача grep - выбрать строки, части которых соответствуют регулярному выражению. В каком месте строки найдено соответствие и каким способом оно достигнуто, для grep неважно, важно только, нашлось или нет. В таком виде эта утилита весьма полезна при работе с системой и применяется повсеместно; нам настолько тяжело было отказаться от нее в наших примерах, что пришлось без особых объяснений ввести ее еще в лекции 6 для ограничения контекста в команде apropos.

Пример. Допустим, мы пишем сценарий на shell, в котором нам понадобится полное имя пользователя, запустившего этот сценарий. Полное имя пользователя (оно же GECOS) хранится в файле /etc/passwd. Для того чтобы извлечь его оттуда, надо знать входное имя пользователя, оно выдается командой logname. На всякий случай набираем в терминале

$ logname
max
$ egrep $(logname) /etc/passwd
max:x:510:500:Max B. Tough:/home/max:/bin/sh
maxim:x:541:500:Maxim Outsider:/home/maxim:/bin/sh
sorrow:x:612:600:Temporary account:/home/shamaxe:/bin/sh

А вот и ошибка! Нас интересует входное имя, а не любая подстрока из /etc/passwd. К счастью, входное имя нетрудно описать в виде регулярного выражения: оно находится в начале строки и заканчивается на " :":

$ egrep "^$(logname):" /etc/passwd
max:x:510:500:Max B. Tough:/home/max:/bin/sh

Теперь осталось выделить из этой строки GECOS ("Max B. Tough"). Это пятое поле /etc/passwd (поля разделяются " :"); ниже будет показано, как выполнить такую задачу при помощи поиска регулярного выражения с заменой, но мы поступим проще: воспользуемся утилитой cut, которая как раз предназначена для вывода некоторых полей. Напишем команду в виде, готовом для сценария (выполним еще одну подстановку и присвоим результат переменной GECOS):

$ GECOS=$(egrep "^$(logname):" /etc/passwd | cut -d: -f5)
$ echo $GECOS
Max B. Tough

Готово!

Регулярными выражениями также пользуются не менее (а если вдуматься - гораздо более) популярная утилита less и ее прародительница more. Мы помним, что обе они предназначены для постраничного просмотра текстовых файлов, только less больше умеет. С некоторых пор к less приделали даже препроцессор - lesspipe ; теперь если на вход less попадет нетекстовый файл, lesspipe превратит его в текстовый - распакует, если файл был упакован, покажет заголовок архива, если это - архив, на худой конец - запустит утилиту strings, которая вынимает из нетекстового файла все строки допустимого формата. В этих утилитах есть команда поиска по тексту - /, поиск вперед (а в less - и ?, поиск назад). Нетрудно догадаться, что в качестве шаблона в этих командах выступают именно регулярные выражения. Либо less либо more используется командой man, когда та показывает руководство, так что поиск в это время может очень пригодиться.

У less, кстати, есть одно незаметное для пользователя, но крайне полезное свойство. Показывая страницу помощи, less, как может, подкрашивает ее (подчеркиванием и ярким шрифтом). Так вот, операция поиска не зависит от того, какая часть найденной строки каким шрифтом набрана. Это совсем не тривиальное свойство, если вспомнить, что в самом отформатированном файле руководства, допустим, яркая буква A присутствует как три символа A+^H+A (напомним, что ^H - условное обозначение символа backspace (ascii-код 8, в восьмеричном виде - 10)), а подчеркнутая буква F - как _+^H+F. Однако, к счастью, команда /bn действительно находит bn в руководстве по less, хотя n выделена подчеркиванием. А grep без посторонней помощи не может справиться с такой задачей, приходится вызывать colcrt(1):

$ man less | colcrt | egrep bn
    -bn or --buffers=n

На вкус и цвет...

Настало время подлить ложку дегтя в бочку меда, которой представляется регулярное выражение. Существует стандарт на регулярные выражения - так называемый POSIX.2. В этот стандарт включены и базовое РВ, и расширенное РВ, что уже создает некоторую неразбериху. К сожалению, стандарт оставляет открытыми некоторые тонкости реализации разбора регулярного выражения, поэтому разные утилиты (точнее, разные библиотеки) могут обрабатывать эти частности по-разному. Не определены до конца всевозможные предположительно ошибочные ситуации, вроде незакрытой скобки или фигурных скобок без цифр внутри (считать их ошибкой или обычными символами?), странные конструкции, наподобие '**' или '+?', и многие \ -последовательности ( '\n' - это n или перевод строки?).

В теоретическом подходе символы, управляющие разбором регулярного выражения - *, $, . и т. п., не принадлежат алфавиту, из которого формируются строки. В действительности алфавит приходится использовать один и тот же. Поэтому поначалу считалось, что только " * ", " ." и [ имеют специальное значение везде в РВ, а " ^ " и " $ " - соответственно в начале и в конце. Все остальные символы считаются обычными, а если они должны быть специальными, тогда перед ними надо поставить " \ ". В базовых РВ это означает, что в группах вместо круглых скобок надо использовать \( и \), а в интервалах вместо фигурных - \{ и \}. В таком виде базовые РВ и вошли в стандарт, и именно так их понимают утилиты grep, sed, more, vi и некоторые другие. Если программа использует библиотеку GNU regexp (в Linux она входит в стандартную библиотеку libc), ситуация сложнее. В GNU regexp формат базового РВ получается из расширенного путем добавления \ ко всем спецсимволам, которые в базовом РВ считаются обычными. В ALT Linux, например, и grep, и sed, и more понимают шаблон 'etc\|bin' и исправно находят строки, в которых есть etc или bin:

linux$ ls -1 / | grep "etc\|bin"
bin
etc
sbin

Наконец, когда программисты расширяют синтаксис регулярного выражения сверх стандарта, они, как правило, не договариваются друг с другом. Так, границы слова в регулярных выражениях, используемых в языках Perl и Python, обозначаются \b (и начало слова, и конец), а в редакторе Vim - \< и \> соответственно. Именно в Perl и Vim, как считается, реализованы самые мощные расширения РВ. Perl regexp существует в виде самостоятельной библиотеки под именем pcre (perl compatible regular expression, см. pcre(3) ). Похожие, тоже весьма мощные, диалекты PB используются в Python и некоторых других языках программирования. Собственный диалект расширенных РВ встроен в интегрированную среду программирования GNU Emacs.

Таким образом, существует несколько стилей оформления регулярных выражений. То, какой именно стиль использует утилита, зависит и от нее самой, и от того, какой regexp-библиотекой она пользуется, и от версии этой библиотеки. Однако путаница эта преодолима. Во-первых, расширенное РВ в различных реализациях практически совпадает. Во-вторых, хотя упомянутые Perl regexp и Vim regexp и отличаются друг от друга и от стандарта, но в них соблюдается совместимость с предыдущими версиями тех же библиотек. Вы можете отказаться от мысли использовать одно и то же регулярное выражение в разных инструментах и задействовать все способности того же Vim, не опасаясь, что к очередной версии или в другой ОС внезапно поменяется его стиль.

< Лекция 13 || Лекция 14: 12345 || Лекция 15 >
Andranik Avakian
Andranik Avakian

41. УК РФ и Комментарии (ст. 273)

М. 2000 г. Издательство: ALT Linux, Институт Логики

Уголовный Кодекс РФ и комментарии к нему?

По ссылке открывается сайт документации Linux, раздел Linux Installation and Getting Started

Сергей Петровский
Сергей Петровский

У Вас написано:

ls -dt1 `grep -il отчет *` | head -1

если знания по шелу мне не изменяют, то должно быть:

ls -dt | `grep -il отчет *` | head -1

Светлана Мишланова
Светлана Мишланова
Россия, Волгоград
Илдар Аллаяров
Илдар Аллаяров
Россия