Одна из наиболее полезных и многофункциональных команд в терминале 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 настолько полезна, многофункциональна и проста в употреблении, что, однажды познакомившись с ней, невозможно представить себе работу без нее.