Ликбез по GREP и регулярным выражениям

Одна из наиболее полезных и многофункциональных команд в терминале Linux – команда «grep». Название представляет собой акроним английской фразы «search Globally for lines matching the Regular Expression, and Print them» (искать везде соответствующие регулярному выражению строки и выводить их). Команда «grep» просматривает входной поток последовательно, строка за строкой, в поисках совпадений и выводит (отфильтровывает) только те строки, которые содержат текст, совпадающий с заданным шаблоном – регулярным выражением.

Регулярные выражения — специальный формальный язык поиска и осуществления манипуляций с подстроками в тексте, основанный на использовании метасимволов. Сейчас уже практически все современные языки программирования имеют встроенную поддержку регулярных выражений для обработки текстов, однако исторически популяризации данного подхода во многом способствовал именно мир UNIX и в частности идеи, заложенные в команды «grep», «sed» и др. Философия «все есть файл» полностью пронизывает UNIX и владение инструментами для работы с текстовыми файлами является одним из обязательных навыков каждого пользователя Linux.

ОБРАЗЕЦ

GIST | Простейший поиск всех строк, в которых есть текст «Adams». При оформлении этого и последующих примеров будем придерживаться следующего порядка: сверху параметры командной строки, внизу стандартные потоки слева ввода stdin и справа вывода stdout.

Adams
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801

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

ОПЦИИ

Начнём с того, что «grep» умеет не только фильтровать стандартный ввод stdin, но и осуществлять поиск по файлам. По умолчанию «grep» будет искать только в файлах, находящихся в текущем каталоге, однако при помощи очень полезной опции --recursive можно сказать команде «grep» искать рекурсивно начиная с заданной директории.

GIST | По умолчанию команда «grep» чувствительна к регистру. Следующий пример показывает как можно искать и при этом не учитывать регистр, например «Adams» и «adams» одно и то же:

--ignore-case 'adams'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801

GIST | Поиск наоборот (иногда говорят инвертный поиск), то есть будут выведены все строки, кроме имеющих вхождение указанного шаблона:

--invert-match 'Adams'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George Washington, 1789-1797
Thomas Jefferson, 1801-1809

GIST | Опции конечно же можно и нужно комбинировать друг с другом. Например поиск наоборот с выводом порядковых номеров строк с вхождениями:

--line-number --invert-match 'Adams'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
1:George Washington, 1789-1797
3:Thomas Jefferson, 1801-1809

GIST | Раскраска. Иногда удобно, когда искомое нами слово подсвечивается цветом. Все это уже есть в «grep», остается только включить:

--line-number --color=always 'Adams'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
2:John Adams, 1797-1801

GIST | Мы хотим выбрать все ошибки из лог файла, но знаем что в следующей сточке после ошибки может содержаться полезная информация, тогда удобно вывести несколько строк из контекста. По умолчанию «grep» выводит лишь строку, в которой было найдено совпадение, но есть несколько опций, позволяющих заставить «grep» выводить больше. Для вывода нескольких строк (в нашем случае двух) после вхождения:

--color=always -A2 'Adams'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
James Madison, 1809-1817
James Monroe, 1817-1825
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
James Madison, 1809-1817

GIST | Аналогично для дополнительного вывода нескольких строк перед вхождением:

--color=always -B2 'James'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
James Madison, 1809-1817
James Monroe, 1817-1825
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
James Madison, 1809-1817
James Monroe, 1817-1825

GIST | Однако чаще всего требуется выводить симметричный контекст, для этого есть ещё более сокращённая запись. Выведем по две строки как сверху так и снизу от вхождения:

--color=always -C2 'James'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
James Madison, 1809-1817
James Monroe, 1817-1825
John Quincy Adams, 1825-1829
Andrew Jackson, 1829-1837
Martin Van Buren, 1837-1841
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
James Madison, 1809-1817
James Monroe, 1817-1825
John Quincy Adams, 1825-1829
Andrew Jackson, 1829-1837

GIST | Когда Вы ищете qwe, то по умолчанию «grep» будет выводить также, qwe123, 345qwerty и тому подобные комбинации. Найдём только те строки, которые выключают именно всё слово целиком:

--word-regexp --color=always 'John'
John Fitzgerald Kennedy, 1961-1963
Lyndon Baines Johnson, 1963-1969
John Fitzgerald Kennedy, 1961-1963

GIST | Ну и напоследок если Вы просто хотите знать количество строк с совпадениями одним единственным числом, но при этом не выводить больше ничего:

--count --color=always 'John'
John Fitzgerald Kennedy, 1961-1963
Lyndon Baines Johnson, 1963-1969
Richard Milhous Nixon, 1969-1974
2

Стоит отметить, что у большинства опций есть двойник, например --ignore-case можно привести к более короткому виду -i и т.д.

БАЗОВЫЕ РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ

Все регулярные выражения состоят из двух типов символов: стандартных текстовых символов, называемых литералами, и специальных символов, называемых метасимволами. В предыдущих примерах поиск осуществлялся по литералам (точное совпадение по буквам), но дальше будет куда интересней. Добро пожаловать в мир регулярных выражений !

Знак каретки ^ и доллара $ имеют в регулярном выражении особый смысл. Их называют «якорями» (anchor). Якоря – это специальные символы, которые указывают местонахождение в строке необходимого совпадения. Когда поиск доходит до якоря, он проверяет, есть ли соответствие, и если есть – продолжает идти по шаблону, не прибавляя ничего к результату.

GIST | Якорь каретка используют чтобы указать, что регулярное выражение необходимо проверить именно с начала строки:

--color=always '^J'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801

GIST | Аналогично якорь доллар стоит использовать в конце шаблона, чтобы указать, что совпадение действительно только если искомая строка символов находится в конце текстовой строки и никак иначе:

--color=always '9$'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
Thomas Jefferson, 1801-1809

GIST | Любой символ. Символ точка используется в регулярных выражениях для того, чтобы обозначить, что в указанном месте может находиться абсолютно любой символ:

--color=always '0.$'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801
Thomas Jefferson, 1801-1809

GIST | Экранирование. Если нужно найти именно символ точка, тогда экранирование в помощь. Знак экранирования (как правило это обратный слеш), предшествующий символу вроде точки, превращает метасимвол в литерал:

--color=always '\.'
George Washington. 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George Washington. 1789-1797

GIST | Классы символов. В регулярных выражениях можно использовать диапазоны и классы символов. Для этого при составлении шаблона используются квадратные скобки. Поместив группу символов (включая символы, которые в противном случае были бы истолкованы как метасимволы) в квадратные скобки, можно указать, что в данной позиции может находиться любой из взятых в скобки символов:

--color=always '0[19]'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801
Thomas Jefferson, 1801-1809

GIST | Диапазон. Это два символа, разделенные дефисом, например, 0-9 (десятичные цифры) или 0-9a-fA-F (шестнадцатеричные цифры):

--color=always '[0-9]'
George Washington, ???
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801
Thomas Jefferson, 1801-1809

GIST | Отрицание. Если первым символом выражения в квадратных скобках является каретка, то остальные символы принимаются как набор символов, которые не должны присутствовать в заданной позиции регулярного выражения:

--color=always '[^7]$'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
John Adams, 1797-1801
Thomas Jefferson, 1801-1809

GIST | Классы символов POSIX. Существует некий набор уже заранее заготовленных классов символов, которые Вы можете использовать в регулярных выражениях. Их там с десяток, достаточно быстро просмотреть мануал чтобы понять назначение каждого. Например отфильтруем только шестнадцатеричные цифры:

--color=always '^[[:xdigit:]]*$'
4.2
42
42abc
42
42abc

GIST | Повторение (0 или больше раз). Одним из наиболее часто используемых метасимволов является символ звёздочка, что означает «повторить предыдущий символ или выражение ноль или больше раз»:

--color=always '^[A-Za-z ,?]*$'
George Washington, ???
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George Washington, ???

Различают базовые регулярные выражения BRE (basic regular expressions) и расширенные ERE (extended regular expressions). В BRE распознаются следующие метасимволы ^ $ . [ ] * и все другие символы расцениваются как литералы. В ERE добавлены ещё такие метасимволы ( ) { } ? + | и связанные с ними функции. Ну а чтобы всех окончательно запутать в «grep» придумали такую штуку – символы ( ) { } в BRE обрабатываются как метасимволы, если они экранированы обратным слешем, в то время как в ERE постановка перед любыми метасимволами обратного слеша приводит к тому, что они трактуются как литералы.

РАСШИРЕННЫЕ РЕГУЛЯРНЫЕ ВЫРАЖЕНИЯ

GIST | Дизъюнкция. Подобно тому, как квадратные скобки задают различные возможные варианты совпадения одного символа, дизъюнкция позволяет указать альтернативные совпадения для строк символов или выражений. Для обозначения дизъюнкции используется символ вертикальной черты:

--extended-regexp --color=always 'George|John'
George Washington, 1789-1797
John Adams, 1797-1801
Thomas Jefferson, 1801-1809
George Washington, 1789-1797
John Adams, 1797-1801

GIST | Совпадение ноль или один раз. В расширенных регулярных выражениях существуют несколько дополнительных метасимволов, указывающих частоту повторения символа или выражения (подобно тому, как метасимвол звездочка указывает на совпадения 0 или более раз). Один из таких метасимволов это вопросительный знак, который делает предыдущий символ или выражение, по сути, необязательными:

--extended-regexp --color=always '^(Andrew )?John'
John Adams, 1797-1801
Andrew Johnson, 1865-1869
Lyndon Baines Johnson, 1963-1969
John Adams, 1797-1801
Andrew Johnson, 1865-1869

GIST | Совпадение один или более раз. Для этого предусмотрен метасимвол в виде знака плюс. Он работает почти как символ звездочка, за исключением того, что выражение должно совпасть хотя бы один раз:

--extended-regexp --color=always '^[[:alpha:] ]+$'
John Adams
Andrew Johnson, 1865-1869
Lyndon Baines Johnson, 1963-1969
John Adams

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

--extended-regexp --color=always
    '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
42
127.0.0.1
127.0.0.1

Команда grep настолько полезна, многофункциональна и проста в употреблении, что, однажды познакомившись с ней, невозможно представить себе работу без нее.

links

social