Материал подготовлен для будущих студентов курса «Администратор Linux. Уровень Про».

Трудно представить современный компьютер или смартфон без тысяч файлов, хранящих в себе различную информацию. Иногда возникает необходимость что-то найти в этом многообразии, причем критерии для поиска могут быть совсем не тривиальными.

В качестве одного из возможных инструментов для поиска может применяться утилита grep. Grep (сокращение от global/regular expression/print) является одним из самых мощных и часто используемых инструментов в арсенале системного администратора и разработчика. На первый взгляд, её задача проста — найти строки в тексте, соответствующие шаблону. Например, вхождение какого-либо слова в текстовом файле Однако истинная мощь grep раскрывается при использовании регулярных выражений (regex).

В этой статье мы разберём три основных типа регулярных выражений, используемых в экосистеме grep: Basic (BRE), Extended (ERE) и Perl-compatible (PCRE) и рассмотрим несколько практических примеров.

Откуда взялись различия?

Чтобы понять разницу, нужно заглянуть в историю. Утилита grep появилась еще в первых версиях Unix. Его синтаксис регулярных выражений стал "базовым" (BRE). Позже были разработаны "расширенные" регулярные выражения (ERE), которые добавляли удобные метасимволы и делали синтаксис более интуитивным. Однако для обратной совместимости стандартный grep по умолчанию остался привязан к BRE. С появлением языка Perl его движок регулярных выражений (PCRE) стал индустриальным стандартом благодаря своей невероятной гибкости и производительности, и современные версии grep (при наличии поддержки) могут использовать и его. Далее мы рассмотрим подробнее каждый из синтаксисов.

Базовые регулярные выражения

Это режим, который используется в grep по умолчанию, если мы не указываем никакие ключи. Главная особенность BRE в том, что некоторые метасимволы (например, ?, +, {, |, ( и )) теряют своё специальное значение и воспринимаются как обычные символы. Чтобы "включить" их специальную функцию, их необходимо экранировать обратным слешем (\).

Рассмотрим пример поиска слова "go" или "goto" с использованием BRE. Обратите внимание на то, что в синтаксисе BRE оператор | (или) — это обычный символ. Чтобы использовать его как логическое "ИЛИ", нужно экранировать.

$ grep 'go\|goto' file.txt

Если мы хотим избежать использования ИЛИ можно использовать такой, менее точный вариант выражения

$ grep 'go\(to\)\?' file.txt # Ищет 'go' или 'goto' (символ 'to' может быть 0 или 1 раз)

И еще один пример. Найдём строки, где буква 'a' встречается ровно 3 раза подряд. В BRE для этого нужно экранировать фигурные скобки.

$ grep 'a\{3\}' file.txt

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

Режим ERE включается ключом -E (или командой egrep, которая ныне считается устаревшей). ERE создан для удобства человека: все стандартные метасимволы (?, +, {, }, |, (, )) работают без экранирования. Это делает синтаксис более чистым и читаемым.

Вернемся к нашему примеру с goto. Вот тот же поиск "go" или "goto" в стиле ERE.

Сравните с примером BRE. Насколько проще и понятнее стал шаблон.

$ grep -E 'go|goto' file.txt

Теперь найдём строки, содержащие последовательности цифр, разделённые точками. {1,3} означает "от 1 до 3 повторений".

$ grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' access.log

Обратите внимание: Мы используем {1,3} без экранирования, а скобки () для группировки частей шаблона.

Perl-совместимые регулярные выражения

Режим PCRE можно по праву назвать вершиной эволюции регулярных выражений в grep. Включить его можно с помощью ключа -P. Он доступен не во всех версиях grep (современные дистрибутивы Linux, как правило, включают поддержку). PCRE предлагает новые возможности, недоступные в BRE и ERE: позитивный и негативный просмотр вперёд/назад (lookahead/lookbehind), "ленивые" квантификаторы, условные выражения, управляющие последовательности (\d, \w, \s) и многое другое.

Рассмотрим еще несколько примеров. Попробуем поискать трехбуквенные слова, которые НЕ являются "the". Для этого воспользуемся негативным просмотром вперёд (negative lookahead): (?!...). 

$ grep -P '\b(?!the)[a-z]{3}\b' text.txt

Здесь \b обозначает границу слова, а (?!the) проверяет, что слово не равно "the", прежде чем [a-z]{3} начнёт его захватывать.

Еще один интересный пример это поиск повторяющихся слов (например, "the the"). Здесь мы используем обратные ссылки и границы слов.

$ grep -P '(\b\w+\b)\s+\1' text.txt

(\b\w+\b) — захватываем целое слово в первую группу.

 \s+ — один или более пробельных символов.

 \1 — обратная ссылка на содержимое первой группы (то же самое слово).

"Ленивый", но не "жадный"

По умолчанию квантификаторы в регулярных выражениях (например, .*) являются "жадными", то есть они захватывают максимально длинную строку. PCRE позволяет сделать их "ленивыми" с помощью ?.

Допустим, есть строка: <div>text1</div><div>text2</div>. "Жадный" поиск (ERE) захватит всё от первого <div> до последнего </div>

$ grep -E '<div>.*</div>' file.html

А вот "ленивый" поиск (PCRE): захватит только <div>text1</div>

$ grep -P '<div>.*?</div>' file.html

Интересные примеры

В завершении давайте применим все знания на реальных задачах, которые могут встретиться в работе. Например, для поиска всех вхождений MAC-адресов в логах мы можем воспользоваться следующим выражением в стиле ERE.

$ grep -E '([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}' system.log

здесь мы ищем вхождения в соответствии с форматом MAC-адреса: 6 групп по 2 шестнадцатеричных символа, разделённых двоеточием.

А для следующей задачи идеально подойдет PCRE. Нам необходимо найти строки, содержащие чётное количество букв 'a'. Мы будем искать строки, где буквы 'a' встречаются группами по 2, 4, 6... раз.

$ grep -P '^(?:[^a]*a[^a]*a[^a]*)*$' file.txt

Более сложный вариант:

$ grep -P '^(?=(?:[^a]*a[^a]*a)*[^a]*$)' file.txt

И еще одна задача для PCRE: извлечение всех URL из текстового файла.

$ grep -P -o 'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+(?::\d+)?(?:/[-\w$.+!*'"(),;:@&=?/~#%]*)?' text_with_links.txt

(Ключ -o выводит только совпадающую часть строки).

Заключение

Итак, давайте подведем итог рассмотренному в сегодняшней статье материалу. Выбор типа регулярных выражений в grep зависит от конкретной задачи. Если нужен быстрый поиск простого слова или шаблона с ^ и $, достаточно BRE по умолчанию.

Если вам нужны операторы |, +, ? и вы не хотите загромождать команду обратными слешами, используйте ERE (grep -E). Это "золотая середина" для 80% повседневных задач.\

Если вам нужно заглянуть вперёд/назад, использовать ленивые квантификаторы или сокращённые классы символов, подключайте тяжёлую артиллерию — PCRE (grep -P).

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

Хотите, чтобы grep и регулярные выражения были не разовой шпаргалкой, а частью ежедневной практики администрирования? На курсе «Администратор Linux. Уровень Про» вы системно прокачаете работу с процессами, дисками, сетью и безопасностью Linux, разберёте типовые сервисы, мониторинг и диагностику в продакшене. Готовы к серьезному обучению? Пройдите вступительный тест.

А чтобы узнать больше о формате обучения и задать вопросы экспертам, приходите на бесплатные уроки:

  • 4 марта в 20:00. «GREP и другие регулярные выражения Linux». Записаться

  • 16 марта в 20:00. «Списывай, но только точь-в-точь: копирование базы данных на примере MySQL». Записаться

  • 24 марта в 20:00. «/proc, /sys, /dev: как Linux превращает железо и процессы в файлы». Записаться

Для тех, кто хочет быстро подтянуть основы, рекомендую мини-видеокурс «Linux для начинающих», сейчас всего за 10 рублей.