Базовый синтаксис
grep [OPTIONS] PATTERN [FILE...]
По умолчанию grep использует BRE (Basic Regular Expressions) - это
значит что +, ?, (, ), {, } в шаблоне трактуются как литералы,
если не экранированы. Чтобы получить «обычные» regex'ы - используй -E
(или egrep, который то же самое):
grep 'foo\+' file # BRE: + надо экранировать
grep -E 'foo+' file # ERE: уже не надо
Главные флаги
| флаг | значение |
|---|---|
-i | без учёта регистра |
-v | инвертировать (строки БЕЗ совпадения) |
-n | с номерами строк |
-c | только количество совпадений |
-l | только имена файлов где есть совпадение |
-L | наоборот - файлы где НЕТ совпадения |
-o | печатать только совпавшую часть |
-w | целое слово |
-x | целая строка |
-r / -R | рекурсивно по дереву (R резолвит symlink'и) |
-A N / -B N / -C N | контекст: After / Before / Around |
--include='*.py' | только эти файлы при -r |
--exclude-dir=.git | пропускать каталог |
Режимы regex
grep -F 'a.b.c' file # -F: фиксированная строка, точки - это точки
grep 'a.b' file # BRE: . = любой символ
grep -E '^[0-9]+$' file # ERE: [], +, {}, () работают без \grep -P '\d{3}-\d{4}' file # PCRE: \d, \w, look-ahead, named groupsPCRE даёт самые мощные шаблоны (lookahead, non-greedy, character classes
как в Perl), но требует чтобы grep был собран с --enable-perl-regexp
(на Ubuntu из коробки - да).
Рекурсивный поиск
grep -rn 'TODO' . # все TODO в проекте, с номерами
grep -rni --include='*.py' 'fixme' src/ # только в .py файлах
grep -rln 'API_KEY' /etc # имена файлов с упоминанием
grep -rn 'password' . --exclude-dir=.git --exclude-dir=node_modules
Без -r grep будет жаловаться: Is a directory.
Контекст
grep -n -B 2 -A 2 'ERROR' app.log # 2 строки до и после
grep -C 3 'panic' /var/log/syslog # ±3 строки вокруг
Якоря и группы
grep '^Host' ~/.ssh/config # начало строки
grep '\.log$' filelist # конец строки
grep -E '\b[0-9]{1,3}(\.[0-9]{1,3}){3}\b' file # IPv4 (см. [[ipv4-addressing]])grep -Eo '[a-z0-9.]+@[a-z0-9.]+' # извлечь email-адреса
Связка с другими инструментами
Самая частая - pipe из cmd-find:
find . -type f -name '*.conf' -print0 | xargs -0 grep -l 'listen 80'
▸найти все конф-файлы где упомянут "listen 80"
Или из вывода другой команды:
ps aux | grep -v grep | grep nginx # классика «без самой grep-строки»
ss -tn | grep ESTAB # ESTABLISHED-сессии
dmesg | grep -i 'oom' # см. [[oom-killer]]
Идиома | grep -v grep нужна потому что сам ps aux | grep nginx находит
собственный процесс grep'а в выводе ps.
Exit codes (важно для скриптов)
0- было хоть одно совпадение1- совпадений не нашлось2- ошибка (нет файла, плохой regex)
Поэтому в bash:
if grep -q ERROR app.log; then
echo "errors present"
fi
-q (quiet) не выводит ничего, только ставит exit code.
ripgrep как современная альтернатива
rg (ripgrep) на порядки быстрее grep -r: автоматически уважает
.gitignore, параллелит, использует SIMD. На больших репах разница
драматическая. Но grep - POSIX и есть везде, поэтому база нужна.