← Весь справочник

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

BRE, ERE и PCRE на практике — классы, якоря, квантификаторы, группы, обратные ссылки, жадность

Регулярное выражение — шаблон, описывающий множество строк. Его понимают grep, sed, awk и многие другие программы, но в нескольких диалектах. Основа у всех общая, отличаются в основном спецсимволы, которые нужно (или не нужно) экранировать.

Три диалекта

  • BRE (базовые) — grep и sed без опций. Символы + ? | ( ) { } здесь обычные; спецсмысл им дают обратные слэши: \+ \? \| \( \) \{ \} (\+ \? \| — расширение GNU).
  • ERE (расширенные) — grep -E, sed -E, awk. Те же + ? | ( ) { } работают без слэшей, а \+ и \( наоборот означают обычные символы.
  • PCRE (Perl) — grep -P. Всё из ERE плюс \d \s \w, ленивые квантификаторы *?, просмотр (?=…) и (?<=…), \K, модификаторы (?i).

Одно и то же в трёх диалектах — «одна или больше цифр, потом точка»:

echo 'v12.3' | grep -o '[0-9]\+\.'      # BRE
echo 'v12.3' | grep -oE '[0-9]+\.'      # ERE
echo 'v12.3' | grep -oP '\d+\.'         # PCRE

Символы и наборы

ЭлементЧто значит
a, 1, -обычный символ совпадает сам с собой
.любой один символ
\. \* \[ \\ \$ \^спецсимвол как обычный
[abc]один символ из набора
[^abc]любой символ, кроме перечисленных
[a-z] [0-9A-F]диапазоны внутри набора
[]a] / [a-] / [.*]] — первым, - — первым или последним; . и * внутри скобок — обычные символы

Классы POSIX

Пишутся внутри квадратных скобок: [[:digit:]], [^[:space:]], [[:alpha:]_].

КлассЧто входит
[:digit:] / [:xdigit:]цифры / шестнадцатеричные цифры
[:alpha:] / [:alnum:]буквы / буквы и цифры
[:upper:] / [:lower:]заглавные / строчные
[:space:] / [:blank:]все пробельные (пробел, \t, \n, \r …) / только пробел и табуляция
[:punct:]знаки препинания
[:print:] / [:graph:] / [:cntrl:]печатные с пробелом / печатные без пробела / управляющие

Сокращения

ЭлементЧто значитГде работает
\w / \Wсимвол слова (буква, цифра, _) / не онGNU grep, sed, gawk, PCRE
\s / \Sпробельный / непробельныйGNU grep, sed, gawk, PCRE
\d / \Dцифра / не цифратолько PCRE (grep -P)
\t \nтабуляция, перевод строкиPCRE, gawk; в sed \n и \t — тоже

Якоря

ЭлементЧто значит
^ / $начало / конец строки
\< / \>начало / конец слова (GNU grep, sed, gawk)
\b / \Bграница слова / не граница (GNU grep, sed, PCRE; в gawk граница — \y)

Квантификаторы

Относятся к предыдущему символу, набору или группе.

ERE / PCREBREСколько раз
**0 и больше
+\+1 и больше
?\?0 или 1
{3}\{3\}ровно 3
{2,5}\{2,5\}от 2 до 5
{2,} / {,5}\{2,\} / \{,5\}2 и больше / не больше 5

Все квантификаторы жадные: берут как можно больше, а потом отступают, если иначе совпадение не найти. В PCRE есть ленивые *?, +?, ??, {2,5}? — они берут как можно меньше.

Группы и альтернатива

ЭлементЧто значит
(…) (в BRE — \(…\))группа: квантификатор действует на всё содержимое, совпавший текст запоминается
\1 … \9обратная ссылка — тот же текст, что совпал с группой. В шаблоне grep и sed, в замене sed; в awk их нет
a|ba или b; внутри группы — (jpg|png). В BRE перед чертой нужен обратный слэш
(?:…)группа без запоминания (PCRE)
(?=…) / (?!…)дальше идёт / не идёт … (PCRE, в совпадение не входит)
(?<=…) / (?<!…)перед этим стоит / не стоит … (PCRE)
\Kзабыть всё, что совпало до этого места (PCRE)
(?i)дальше без учёта регистра (PCRE)

Примеры

# строка целиком из цифр
printf '123\n12a\n' | grep -E '^[0-9]+$'

# пустые строки и строки из одних пробелов
printf 'a\n\n  \nb\n' | grep -c '^[[:space:]]*$'

# строки, где первый символ — не пробел и не #
printf '# comment\n  indented\nvalue\n' | grep '^[^[:space:]#]'

# целое слово: cat, но не catalog
echo 'cat catalog' | grep -o '\<cat\>'
echo 'cat catalog' | grep -oE '\bcat\b'

# необязательная буква и точное число повторов
echo 'color colour' | grep -oE 'colou?r'
echo '2024-01-05' | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'

# альтернатива и группа с квантификатором
printf 'ab\nabab\naba\n' | grep -xE '(ab)+'
printf 'a.jpg\nb.png\nc.txt\n' | grep -E '\.(jpg|png)$'

# обратная ссылка: два одинаковых символа подряд
echo 'aa bc dd' | grep -oE '(.)\1'

# группы в замене: переставить части даты
echo '2024-01-05' | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3.\2.\1/'

# жадность: .* захватывает максимум
echo '<a><b>' | grep -oE '<.*>'
# ленивый вариант есть только в PCRE
echo '<a><b>' | grep -oP '<.*?>'
# переносимая замена ленивости — класс-отрицание
echo '<a><b>' | grep -oE '<[^>]*>'

# PCRE: просмотр назад и \K — совпадение без префикса
echo 'id=42' | grep -oP '(?<=id=)\d+'
echo 'id=42' | grep -oP 'id=\K\d+'

# PCRE: слово, после которого НЕ идёт цифра
echo 'v1 vx' | grep -oP 'v(?!\d)\w'

# спецсимвол как обычный: точка и звёздочка
printf '1.5\n105\n' | grep '^1\.5$'
printf 'a*b\naab\n' | grep 'a\*b'

Подводные камни

  • Точка — любой символ. grep '1.2' найдёт и 1x2. Экранируй (1\.2), бери в скобки (1[.]2) или используй grep -F для поиска строки как есть.
  • Классы POSIX — только внутри скобок. [:digit:] без вторых скобок означает набор символов : d i g t; GNU grep в этом случае останавливается с ошибкой character class syntax is [[:space:]], not [:space:].
  • \d вне -P — это буква d, а \t в grep — буква t. Пиши [0-9] или [[:digit:]], а табуляцию передавай как $'\t'.
  • Нет ленивых квантификаторов в BRE/ERE. .*? в grep -E и sed -E работает как обычный жадный .*. Используй [^…]* или grep -P.
  • ^ и $ в BRE специальны только по краям. grep 'a^b' ищет буквальное a^b.
  • * в начале шаблона — обычный символ в BRE: grep '*a' ищет *a. В ERE GNU grep принимает его с предупреждением * at start of expression.
  • Регулярка ищет подстроку. grep '[0-9]' найдёт строку abc1. Чтобы проверить строку целиком, ставь ^…$ или grep -x.
  • Шаблон в кавычках. Без одинарных кавычек оболочка сама обработает *, ?, […], $ и \.
  • Диапазоны и . зависят от локали. В песочнице локаль POSIX: . — один байт, [a-z] — ровно латинские строчные, а [[:alpha:]] не включает кириллицу. С LC_ALL=C.utf8 . совпадает с целой буквой, и [[:alpha:]] находит ё.

См. также

grep · sed · awk