Регулярные выражения для grep
Зачем это нужно
В главе про терминал мы обходились командами над файлами целиком: показать, скопировать, удалить. Но на чемпионате и в работе постоянно нужна операция потоньше — найти в большом файле нужные строки. Лог веб-сервера на десять тысяч строк, в котором нужно выловить только ошибки. Конфиг, в котором нужно найти строку с портом. Чужой проект, в котором нужно понять, где вообще используется функция с таким-то именем. Листать глазами — не вариант; для этого есть grep.
grep расшифровывается как global regular expression print — «напечатать по глобальному регулярному выражению». Название выдаёт главное: сила grep не в поиске точного текста (это он тоже умеет), а в поиске по шаблону — регулярному выражению (regular expression, коротко regex). Шаблон описывает не конкретную строку, а правило: «строка, начинающаяся с цифры», «слово error в любом регистре», «три цифры, дефис, ещё две цифры». Один раз научившись читать и писать такие правила, ты получаешь суперспособность, которая работает далеко за пределами grep: те же регулярные выражения понимают редакторы кода, языки программирования и половина инструментов командной строки.
Эта страница — углубление: базовый маршрут от неё не зависит, но перед ней стоит пройти главу «Linux и терминал», чтобы команды, флаги и конвейер | уже не были новостью.
grep без регулярок: точный текст и главные флаги
Начнём с того, что regex ещё не требует. В простейшей форме grep получает текст для поиска и файл — и печатает все строки, в которых этот текст встречается:
grep error app.log
Наведи курсор на части команды посложнее — с флагами, которые понадобятся в первую очередь:
Флаги, без которых не живёт ни один день с grep:
-i— игнорировать регистр. Ошибки в логах пишут кто во что горазд:Error,ERROR,error— с-iнайдутся все.-n— показывать номера строк. Нашёл строку — сразу знаешь, куда идти в редакторе.-v— инвертировать поиск: напечатать строки, где шаблона нет.grep -v '#' config.txt— конфиг без строк с решёткой.-c— не печатать строки, а посчитать их:grep -c error app.logответит одним числом.-w— искать только целые слова:grep -w logнайдётlog, но неloginи неcatalog.-r— искать рекурсивно по всем файлам каталога:grep -r TODO src/— все заметки TODO во всём проекте.
И приём из главы про терминал: grep умеет читать не только файлы, но и вывод другой команды через конвейер — cat app.log | grep error даёт тот же результат, а history | grep ssh найдёт все команды ssh, которые ты набирал.
Точка, экранирование и классы символов
Теперь сама магия. В регулярном выражении часть символов — не буквы, а операторы. Первый из них — точка: . означает «ровно один любой символ». Шаблон v.sion найдёт и vision, и version... стоп, нет: version не найдёт — между v и sion там два символа. А вот v..sion — найдёт version, потому что двум точкам соответствуют er.
Отсюда сразу неприятный сюрприз: если искать точку буквально — например, версию 1.5 или расширение .txt — шаблон 1.5 найдёт заодно 105, 1x5 и вообще «1, что угодно, 5». Чтобы оператор снова стал обычным символом, его экранируют обратной косой чертой: 1\.5 — это уже строго «единица, точка, пятёрка».
Второй оператор — класс символов: квадратные скобки со списком допустимых символов внутри. [abc] — «один символ: a, b или c». Внутри класса работает диапазон через дефис: [0-9] — любая цифра, [a-z] — любая строчная латинская буква, [A-Za-z0-9] — буква или цифра. А если первый символ в скобках — крышка, класс инвертируется: [^0-9] — «один любой символ, кроме цифры».
Обрати внимание на кавычки вокруг шаблона. Символы *, [, ], $ — операторы не только для grep, но и для самой оболочки bash (вспомни подстановку *.txt из главы про терминал). Одинарные кавычки говорят оболочке: «не трогай, передай как есть». Правило простое: шаблон со спецсимволами — всегда в одинарных кавычках; за привычку брать в кавычки вообще любой шаблон никто не осудит.
Якоря: начало и конец строки
По умолчанию grep ищет совпадение где угодно в строке. Якоря (anchors) прибивают шаблон к месту: ^ — к началу строки, $ — к концу.
grep '^From' mail.txt— строки, начинающиеся сFrom; письмо, гдеFromвстречается в середине текста, не подойдёт.grep 'done$' tasks.txt— строки, заканчивающиеся наdone.grep '^$' file.txt— пустые строки: начало строки, сразу за ним конец, между ними ничего. Аgrep -c '^$' file.txtих посчитает.grep -v '^#' config.txt— фильтр комментариев поточнее, чем раньше: выкидываются только строки, где#стоит первым символом, а строки, где решётка встречается в середине, остаются.
Якорь — не символ строки, а позиция: ^ не ищет никакую «крышку» в тексте, он утверждает «здесь начало». Поэтому шаблон ^$ находит строку нулевой длины, хотя в нём два символа.
Квантификаторы: сколько раз повторить
Последний кирпич — квантификаторы (quantifiers), операторы «сколько раз повторяется предыдущий элемент»:
*— ноль или больше раз.ab*cнайдётac,abc,abbbbc.+— один или больше раз.ab+cнайдётabcиabbbc, но уже неac.?— ноль или один раз.colou?rнайдёт иcolor, иcolour.{n}— ровно n раз,{n,m}— от n до m раз.[0-9]{4}— ровно четыре цифры подряд.
Квантификатор относится к одному предыдущему элементу — символу или классу. [0-9]+ — «одна или больше цифр», а 10* — это «1, а потом сколько угодно нулей» (10, 100, 1000...), не «десять сколько-то раз».
Тонкость, о которую спотыкаются все: исторически у grep два диалекта регулярных выражений. В базовом (BRE — basic regular expressions), который включён по умолчанию, операторы +, ?, {} нужно писать с обратной косой: \+, \?, \{4\}. В расширенном (ERE — extended regular expressions) они работают как есть, но нужен флаг -E. Проще всего запомнить одно правило: пользуешься +, ? или фигурными скобками — добавляй -E и пиши по-человечески.
Практика: grep-квест в терминале
Применим всё в учебном терминале. Честное предупреждение: grep здесь упрощённый — он ищет подстроку и не разбирает регулярные выражения, так что операторы из этой страницы отрабатывай потом в настоящем терминале. Зато флаги команд, конвейер и сама логика «найди иголку в стоге» — как настоящие.
В файле ~/logs/app.log среди сотни строк спрятана одна со словом ACCESS — в ней написан секретный код. Найди её c помощью grep (или cat app.log | grep ...), прочитай код и создай в домашней папке пустой файл, названный этим кодом: если код apple, то touch found_apple.txt.
- ○ found_cherry.txt
Как это устроено под капотом
И закрепи руками написание настоящих шаблонов — с регистром, кавычками и косыми всё должно быть точно до символа:
Цель: точность ≥ 90%
grep -in error app.log
Шпаргалка
| Запись | Что означает | Пример | Найдёт |
|---|---|---|---|
abc | буквально этот текст | grep error log | строки со словом error |
. | один любой символ | v..sion | version, но не vision |
\. | буквально точка | 1\.5 | 1.5, но не 105 |
[abc], [0-9] | один символ из набора/диапазона | v[0-9] | v1, v2... |
[^0-9] | один символ НЕ из набора | id[^0-9] | idx, но не id7 |
^, $ | начало / конец строки | ^From, done$ | позиция, не символ |
^$ | пустая строка | grep -c '^$' f | посчитать пустые |
* | предыдущий 0+ раз | ab*c | ac, abc, abbc |
+ (с -E) | предыдущий 1+ раз | ab+c | abc, но не ac |
? (с -E) | предыдущий 0–1 раз | colou?r | color и colour |
{n}, {n,m} (с -E) | ровно n / от n до m раз | [0-9]{4} | четыре цифры подряд |
-i / -n / -v | без регистра / с номерами / инверсия | grep -inv | — |
-c / -w / -r | посчитать / целые слова / рекурсивно | grep -rw log . | — |
-E | расширенный диалект ERE | grep -E '[0-9]{3}' codes.txt | включай для + ? |
Углубиться
Третий диалект: PCRE. Кроме BRE и ERE, у GNU grep есть флаг -P — Perl-совместимые регулярные выражения, самый богатый диалект: сокращения вроде \d (цифра) и \w (символ слова), ленивые квантификаторы, просмотры вперёд-назад. Именно PCRE-подобный синтаксис используют регулярки в Python, JavaScript и большинстве языков. В скриптах, которые должны работать на любой системе, на -P полагаться нельзя — он есть не во всех сборках grep, — но на своей машине это самый удобный режим.
POSIX-классы. Внутри квадратных скобок работают именованные классы: [[:digit:]] — то же, что [0-9], [[:alpha:]] — буквы, [[:space:]] — пробельные символы. Выглядят громоздко, но корректно работают с любыми локалями и алфавитами — в отличие от диапазона [a-z], который про кириллицу ничего не знает.
egrep и fgrep. В старых скриптах встречаются команды egrep (то же, что grep -E) и fgrep (то же, что grep -F — искать буквально, вообще без регулярных выражений). Они объявлены устаревшими: POSIX рекомендует формы с флагами, и новые скрипты стоит писать именно так.
-o — показать только совпадение. Обычно grep печатает строку целиком. Флаг -o печатает только совпавший кусок, каждое совпадение на своей строке: grep -oE '[0-9]+' log.txt вытащит из файла все числа. В связке с wc -l — готовый счётчик совпадений (не строк, как -c, а именно совпадений).
Дальше по теме:
- GNU Grep Manual — Regular Expressions — официальное руководство: точное описание BRE, ERE и PCRE со всеми операторами
- man 7 regex — справка по регулярным выражениям POSIX, общая для grep, sed и других утилит
Что должен уметь
- Искать текст в файле и в выводе другой команды, применяя флаги
-i,-n,-v,-c,-w,-rпо назначению. - Объяснять, почему шаблон берут в одинарные кавычки и что случится без них.
- Читать и писать шаблоны с точкой, экранированием, классами
[...]и инвертированными классами[^...]. - Прибивать шаблон якорями
^и$— в том числе находить пустые строки и строки-комментарии. - Пользоваться квантификаторами
*,+,?,{n,m}и знать, когда нужен флаг-E. - Отличать диалекты BRE и ERE и объяснять, почему
grep '{4}'иgrep -E '{4}'ведут себя по-разному.
Куда дальше: проверенные ресурсы
- GNU Grep Manual — первоисточник: все флаги и все три диалекта регулярных выражений в одном документе.
- man grep (Ubuntu) — официальная man-страница: краткая версия того же, всегда под рукой по команде
man grep.
- Видео и материалы сообщества — ниже — ролики по теме и ссылки от студентов и преподавателей смотри в самом низу страницы.
Имя grep — буквально команда из редактора ed 1970-х: g/re/p, «глобально найти по regular expression и напечатать». Кен Томпсон вынес её в отдельную программу — и она пережила сам редактор на полвека.
Комментарии
Комментарии появятся после настройки. Нужен аккаунт GitHub — вход прямо в виджете выше.