Недавно была неприятная проблема. Горело. Почитал. Решил поделиться.
Регулярные выражения в sed: BRE vs ERE
sed по умолчанию использует BRE (Basic Regular Expressions — базовые регулярные выражения по стандарту POSIX).
Что это означает на практике: скобки (){} нужно экранировать, если хочешь использовать их как спецсимволы регэкспа (группа, квантификатор). Без экранирования они воспринимаются как обычные символы.
Специальное значение в BRE имеют только:
$.*[\]^
Чтобы использовать их как литералы — экранируй через \. А вот ?, +, | в BRE вообще нет.
Это контринтуитивно: чтобы () стали спецсимволами — их надо экранировать, а чтобы $ стал обычным символом — тоже экранировать. Логики немного, зато традиций хоть отбавляй.
Флаг -r спасает
К счастью, у sed есть ключ -r, который включает ERE (Extended Regular Expressions — расширенные регулярные выражения).
В ERE:
(){}работают как спецсимволы без экранирования- Появляются
+,?,| - Обратные ссылки
\1,\2и т.д. в строке замены (правая часть) — работают &в строке замены — ссылается на всё найденное совпадение целиком
Большинство руководств, онлайн-парсеров и туториалов используют синтаксис ERE — поэтому его и стоит запоминать как основной.
Про PCRE: есть ещё перловые регулярки (PCRE), говорят — огонь. Но для базового использования в шелле их синтаксис совпадает с ERE, так что разница несущественна.
Экранирование в bash
Вот где начинается настоящее веселье. Мы используем sed внутри bash, а у bash есть собственные спецсимволы, которые он интерпретирует ещё до того, как команда вообще до sed доберётся.
В bash три вида экранирования:
1. Побуквенное (обратный слеш)
Берём список спецсимволов bash:
# & * ? [ ] ( ) = | ^ ; < > ` $ " ' \
И ставим перед каждым \, если хотим его как литерал. Неудобно для длинных строк, зато универсально.
2. Слабое экранирование — двойные кавычки "
Отключает все спецсимволы кроме $, `, \. То есть переменные шелла ($VAR) и подстановки команд (`cmd`) внутри двойных кавычек всё ещё работают.
3. Сильное экранирование — одинарные кавычки '
Отключает всё. Буквально всё — никакой интерпретации. Единственное ограничение: одинарную кавычку внутри одинарных кавычек не вставить (ведь она завершает строку).
Главное правило: какие кавычки использовать с sed
┌─────────────────────────────────────────────────────┐
│ Нет переменных шелла, нет одинарных кавычек в паттерне?
│ → sed -r '...' (одинарные кавычки, ERE)
│
│ Нужна переменная $VAR или одинарная кавычка в паттерне?
│ → sed -r "..." (двойные кавычки, ERE)
└─────────────────────────────────────────────────────┘
Повторим ещё раз:
# Стандартный случай — одинарные кавычки
sed -r 's/foo (bar)/\1/'
# Нужна переменная или одинарная кавычка в паттерне — двойные
sed -r "s/foo/$VAR/"
Шпаргалка: BRE vs ERE
| Возможность | BRE | ERE (-r) |
|---|---|---|
| Группа | \( \) |
( ) |
| Квантификатор | \{ \} |
{ } |
| Один или больше | ✗ | + |
| Ноль или один | ✗ | ? |
| Альтернатива | ✗ | | |
| Обратные ссылки в замене | \1 |
\1 |
Полезные флаги и команды sed
# Замена первого вхождения в каждой строке
sed -r 's/foo/bar/'
# Замена всех вхождений (флаг g)
sed -r 's/foo/bar/g'
# Изменить файл на месте (флаг -i)
sed -r -i 's/foo/bar/g' file.txt
# Разделитель можно менять — удобно если в паттерне есть /
sed 's~foo/bar~baz~'
sed 's|foo/bar|baz|'
Список литературы
- UNIX Power Tools — ch.08 par.14
- Bash Hackers Wiki — Quoting
- StackExchange — What characters do I need to escape when using sed in a sh script?
- Wikipedia — POSIX basic and extended regular expressions
- Википедия — Регулярные выражения