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

Материал из llmdocs — справочник разработчика
grep и регулярные выражения
Типутилита командной строки
АвторКен Томпсон
Появилась1973 (Unix Version 4)
Названиеот команды ed g/re/p
РеализацииGNU grep, BSD grep, BusyBox, ripgrep (rg)
Синтаксисы шаблоновBRE, ERE (-E), PCRE (-P), фиксированные строки (-F)
СтандартPOSIX

grep — утилита командной строки Unix, которая выводит строки файлов (или стандартного ввода), соответствующие заданному шаблону. Шаблоны записываются регулярными выражениями — компактным языком описания множеств строк. grep — один из самых часто используемых инструментов в Linux: им ищут ошибки в логах, вызовы функций в коде, строки конфигурации и фильтруют вывод других программ в конвейерах.

Название происходит от команды текстового редактора ed g/re/p — «глобально найти по регулярному выражению (regular expression) и напечатать». Слово «грепнуть» давно стало глаголом в жаргоне программистов.

История

Утилиту написал Кен Томпсон, один из создателей Unix; она вошла в Unix Version 4 (1973). Томпсон и раньше занимался регулярными выражениями: в 1968 году он опубликовал алгоритм их компиляции в автомат, который и сегодня лежит в основе быстрых движков. Позже появились варианты egrep (расширенные выражения) и fgrep (фиксированные строки); теперь вместо них используют ключи grep -E и grep -F.[1] Синтаксис выражений и поведение grep закреплены в стандарте POSIX.[3]

Основные ключи

grep ERROR app.log                 # строки, содержащие ERROR
grep -i error app.log              # без учёта регистра
grep -v DEBUG app.log              # строки, НЕ содержащие DEBUG
grep -n 'def main' *.py            # с номерами строк
grep -c ERROR app.log              # только количество совпадений
grep -rn 'TODO' src/               # рекурсивно по каталогу
grep -rl 'api_key' .               # только имена файлов
grep -w log file.txt               # целое слово: log, но не login
grep -o '[0-9]\+ms' app.log        # вывести только совпавшую часть
grep -A3 -B1 Traceback app.log     # 3 строки после и 1 до совпадения
grep -F '[x]' list.md              # шаблон как обычная строка, без спецсимволов
ps aux | grep -v grep | grep nginx # фильтр вывода другой команды

Код выхода grep — 0, если что-то найдено, 1, если ничего, и 2 при ошибке; на этом строятся проверки в скриптах (Shell и Bash): if grep -q ready status.txt; then ....

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

Регулярное выражение — шаблон из обычных символов и метасимволов. Подробная теория — в статье Регулярные выражения; ниже — то, что нужно для grep.

Конструкция Смысл Пример
. любой символ h.t — hat, hot, h1t
^, $ начало и конец строки ^# — строки-комментарии
[abc], [^0-9] символ из набора / не из набора [Ee]rror
* 0 и более повторений ab*c
+, ? 1 и более / 0 или 1 (в ERE) colou?r
{n,m} от n до m повторений (в ERE) [0-9]{3}
| альтернатива (в ERE) ERROR|FATAL
( ) группировка (в ERE) (ab)+
[[:digit:]], [[:space:]] классы символов POSIX

BRE, ERE и PCRE

grep понимает несколько диалектов:[2]

  • BRE (базовые, по умолчанию) — +, ?, |, {} и () считаются обычными символами, а для специального значения их нужно экранировать: \+, \(. Это источник постоянной путаницы.
  • ERE (расширенные, grep -E) — метасимволы работают без обратной косой черты. Для новых шаблонов удобнее всего.
  • PCRE (grep -P, только GNU grep) — синтаксис Perl: \d, \w, нежадные квантификаторы *?, просмотр вперёд и назад (?=...).[5] Совпадает с привычными выражениями Python и JavaScript.
grep -E '^(ERROR|WARN) ' app.log                     # ERE
grep -E '\b[0-9]{1,3}(\.[0-9]{1,3}){3}\b' access.log  # похожее на IPv4
grep -oP '(?<=user_id=)\d+' app.log                   # PCRE: число после user_id=

Шаблон почти всегда стоит брать в одинарные кавычки: иначе оболочка сама раскроет *, $ и \ до того, как их увидит grep.

ripgrep и другие альтернативы

Для поиска по коду популярна утилита ripgrep (rg) Эндрю Галланта, написанная на Rust (2016). Она по умолчанию ищет рекурсивно, пропускает файлы из .gitignore и двоичные файлы, использует все ядра процессора и обычно заметно быстрее grep -r на больших репозиториях.[4] Для структурного поиска по коду применяют ast-grep, для поиска внутри JSON — jq, а для редактирования найденного — sed и awk.

rg 'fn main' -t rust          # только Rust-файлы
rg -l TODO                    # файлы с TODO, с учётом .gitignore

Типичные ошибки

  • grep '*.log' — * в регулярном выражении означает «повторить предыдущее», а не «любые символы», как в шаблонах имён файлов оболочки.
  • Точка в IP-адресе или имени домена совпадает с любым символом; её нужно экранировать (\.) или использовать -F.
  • Конструкция cat file | grep x работает, но лишний cat не нужен: grep x file.
  • В выводе grep -r по огромным каталогам (node_modules, .git) теряются нужные совпадения — помогает --exclude-dir или ripgrep.

См. также

Источники

  1. GNU grep manual
  2. POSIX.1-2024 — Regular Expressions
  3. POSIX.1-2024 — grep
  4. ripgrep — GitHub
  5. PCRE — Perl Compatible Regular Expressions