1. Регулярные выражения: фундамент и базовый синтаксис BRE
Регулярные выражения: фундамент и базовый синтаксис BRE
Представьте, что перед вами текстовый лог веб-сервера размером 50 гигабайт. Ваша задача — найти все запросы от подсети 192.168.1.x, которые завершились 404-й ошибкой, причем сделать это нужно быстро, прямо в консоли сервера. Обычный текстовый поиск здесь бессилен: запрос 192.168.1. найдет и нужный 192.168.1.15, и совершенно чужой 10.192.168.120, и даже 192.168.100.5. Строгий поиск по конкретному IP не сработает, потому что адресов в подсети двести пятьдесят четыре. В этот момент администратор переходит от поиска конкретных слов к поиску структур.
Регулярные выражения (Regular Expressions, или Regex) — это специализированный язык, позволяющий описать форму, шаблон или геометрию текста. Это не просто инструмент поиска, это способ объяснить компьютеру концепции вроде «число из четырех цифр», «слово, начинающееся с заглавной буквы» или «пустая строка».
В экосистеме Linux существует несколько стандартов регулярных выражений. Мы начнем с фундамента — стандарта BRE (Basic Regular Expressions). Именно он используется по умолчанию в классических утилитах grep, sed и редакторе vi. Понимание BRE — это билет к свободному чтению конфигураций и логов в любой UNIX-подобной системе, даже самой старой или урезанной.
Литералы и Метасимволы: Две роли символов
Любое регулярное выражение состоит из символов, которые делятся на две строгие категории: литералы и метасимволы.
Литералы — это символы, которые означают сами себя. Буква a ищет букву «a», цифра 5 ищет цифру «5». Если ваше регулярное выражение состоит только из литералов (например, error), оно работает как обычный текстовый поиск.
Метасимволы — это управляющие символы. Они не ищут самих себя в тексте, а отдают команды регулярному движку (программе, которая выполняет поиск). В стандарте BRE базовых метасимволов немного: ^, ` (доллар) означает конец строки.
Выражение failed найдет строку, в которой есть только слово «error» и больше ничего. А выражение ^ работают как якоря только тогда, когда находятся на соответствующих краях выражения. Если вы напишете a^b, карет потеряет свою магическую силу и превратится в обычный литерал — будет искаться буквально строка «a^b».
Точка: Универсальный заместитель
Метасимвол . (точка) — это джокер. Он заменяет ровно один абсолютно любой символ, кроме символа переноса строки.
Если вы напишете шаблон c.t, он совпадет с:
cat (точка заменила 'a')
cot (точка заменила 'o')
c3t (точка заменила '3')
c t (точка заменила пробел)
c-t (точка заменила дефис)
Но этот шаблон не совпадет с:
(между 'c' и 't' нет символа, а точка требует ровно один символ) (между 'c' и 't' два символа, а точка заменяет только один)!Пошаговое совпадение шаблона с точкой
Точка — невероятно мощный, но опасный инструмент. Начинающие администраторы часто используют ее из-за лени, когда не хотят точно описывать нужный символ. Например, пытаясь найти IP-адрес 10.0.0.1, пишут grep '10.0.0.1'. Но поскольку точка в Regex — это любой символ, этот шаблон радостно найдет и 10.0.0.1, и 1050x0a1, где точки сработали как метасимволы, заменив пятерку, икс и букву 'a'. Как искать саму точку как знак препинания, мы разберем в разделе об экранировании.
Символьные классы: Задание допустимых множеств
Если точка позволяет подставить любой символ, то символьные классы (character sets) позволяют задать жесткий список того, что может находиться в данной позиции. Класс создается с помощью квадратных скобок [].
Выражение [aeiou] соответствует ровно одному символу, если этот символ — гласная буква из списка.
Шаблон c[aou]t совпадет с cat, cot и cut, но проигнорирует cet или c3t.
Внутри квадратных скобок метасимволы теряют свою силу. Точка внутри класса [.] означает просто точку как знак препинания.
Диапазоны
Перечислять все цифры или буквы алфавита утомительно, поэтому внутри классов поддерживаются диапазоны через дефис -.
[0-9] — любая цифра от 0 до 9.
[a-z] — любая строчная буква английского алфавита.
[A-Z] — любая заглавная буква.
[a-zA-Z0-9] — любая английская буква (в любом регистре) или цифра.Отрицание класса
Если первым символом внутри квадратных скобок поставить карет ^, класс инвертируется. Он будет искать ровно один символ, которого нет в указанном списке.
Шаблон [^0-9] найдет любой символ, кроме цифры. Это может быть буква, пробел, знак препинания — что угодно, только не цифра. Обратите внимание: вне скобок ^ означает начало строки, а внутри скобок (только на первой позиции) — отрицание множества.POSIX-классы
При работе с диапазонами вроде [a-z] возникает проблема локали (языковых настроек системы). В некоторых локалях этот диапазон может захватить символы с диакритическими знаками или буквы других алфавитов. Чтобы сделать регулярные выражения переносимыми и надежными, стандарт POSIX ввел именованные классы.Они выглядят как
[:имя:] и всегда используются внутри обычных квадратных скобок (получаются двойные скобки):
[[:digit:]] — эквивалент [0-9] (любая цифра).
[[:alpha:]] — любая буква в любом регистре.
[[:alnum:]] — любая буква или цифра.
[[:space:]] — любой пробельный символ (пробел, табуляция).
[[:punct:]] — любой знак пунктуации.Пример: шаблон
^[[:upper:]] найдет строки, которые начинаются с любой заглавной буквы, независимо от того, английский это алфавит, русский или греческий (при правильно настроенной системной локали).Квантификатор *: Умножение сущностей и ловушка глоббинга
До сих пор все наши метасимволы (точка, классы) описывали ровно один символ. Но тексты состоят из последовательностей неизвестной длины. Для управления количеством повторений используются квантификаторы.
В стандарте BRE есть только один базовый квантификатор, не требующий специального синтаксиса — это звездочка
*.
Метасимвол * означает: предыдущий элемент может повторяться ноль или более раз.Разберем это определение по частям.
«Предыдущий элемент» — это символ или класс символов, который стоит непосредственно перед звездочкой.
В выражении
ab*c звездочка относится только к букве b.«Ноль или более раз» — самая контринтуитивная часть для новичков. Шаблон
ab*c совпадет с:
abbbc (буква 'b' повторилась 3 раза)
abc (буква 'b' повторилась 1 раз)
ac (буква 'b' повторилась 0 раз — ее вообще нет, и это легально для Regex!)Если мы применим звездочку к символьному классу:
[0-9]*, это будет означать «любое количество цифр подряд, либо полное их отсутствие».Критическое различие: Regex vs Shell Globbing
Здесь кроется главная ловушка для системных администраторов. В командной оболочке Bash мы привыкли использовать звездочку для поиска файлов:
ls .txt. Там звездочка означает «любая последовательность любых символов». Этот механизм называется глоббингом* (Globbing).В регулярных выражениях * — это лишь множитель. Сама по себе звездочка в Regex не означает «любые символы». Она лишь умножает то, что стоит перед ней.
Чтобы получить в регулярных выражениях аналог башевского * (любой текст), нужно скомбинировать точку (любой символ) и звездочку (ноль или более раз).
Комбинация .* — это абсолютный поглотитель. Она означает «любой символ, повторенный любое количество раз».
Сравните:
| Задача | В Bash (Globbing) | В grep (Regex) |
| :--- | :--- | :--- |
| Найти все, что начинается с "log" | ls log | grep '^log.' |
| Найти все файлы .conf | ls *.conf | grep '\.conf
Итоговый шаблон для grep:
grep '^\[[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\] CRITICAL.*kernel$' /var/log/syslog
На первый взгляд это выглядит как бессмысленный набор символов. Но теперь вы можете прочитать его как парсер: символ за символом, понимая назначение каждого якоря, экранирования, класса и квантификатора.
Регулярные выражения требуют изменения мышления. Вы больше не ищете текст глазами — вы описываете математическую модель строки. Базовый стандарт BRE закладывает фундамент этого мышления. Ограничения BRE (например, необходимость писать [0-9]` четыре раза подряд) решаются в стандарте ERE (Extended Regular Expressions), где появляются метасимволы группировки и точного указания количества повторений.