grep в Linux: поиск по тексту с регулярками и примерами для Ubuntu
grep — главный инструмент поиска по содержимому файлов в Linux. Умеет работать с регулярными выражениями POSIX и PCRE, рекурсивно обходить директории, считать совпадения, показывать контекст вокруг строки. Разбираем синтаксис, ключевые флаги, тонкости с регулярками и десяток рецептов для повседневной работы администратора Ubuntu.
Зачем нужен grep
grep (Global Regular Expression Print) — стандартная утилита поиска текстовых строк по шаблону. Установлена в Ubuntu из коробки. В отличие от find, который ищет файлы, grep ищет строки внутри файлов или потока — это разные задачи, и они часто используются в связке.
Что делает grep:
- Ищет вхождения подстроки или регулярки в файле, потоке (stdin) или рекурсивно в дереве каталогов.
- Печатает строки, в которых нашёл (или, наоборот, не нашёл —
-v). - Может показывать только имена файлов с совпадениями, количество совпадений, номера строк, контекст вокруг.
- Поддерживает три диалекта регулярок: BRE (по умолчанию), ERE (
-E), PCRE (-P).
Базовый синтаксис
grep [опции] <шаблон> [файл...]
Простейший вызов:
grep "TODO" main.py
Шаблон — в кавычках, чтобы спецсимволы не интерпретировал shell. Если файл не указан — grep читает stdin (полезно для конвейеров).
Топ-10 флагов на каждый день
| Флаг | Что делает |
|---|---|
-r, -R |
рекурсивно обойти директорию |
-i |
без учёта регистра |
-v |
инвертировать — показать строки без совпадения |
-n |
вывести номер строки |
-c |
вывести только количество совпадений |
-l |
вывести только имена файлов с совпадениями |
-L |
наоборот — файлы без совпадений |
-w |
только целые слова (с границей слова \b) |
-x |
только целые строки (вся строка должна совпасть) |
-q |
quiet, ничего не выводить, только exit code |
-A N |
показать N строк после совпадения |
-B N |
N строк до |
-C N |
N строк до и после |
-o |
вывести только совпавшую часть, не всю строку |
--include="*.py" |
искать только в файлах по маске |
--exclude-dir=.git |
пропустить директорию |
Эти 16 флагов покрывают 95% реальных задач.
Регулярные выражения: BRE, ERE, PCRE
grep поддерживает три диалекта.
BRE — Basic Regular Expressions (по умолчанию)
Базовые метасимволы работают только если экранированы:
grep "error\\|warning" app.log # OR в BRE — экранируется
grep "ab\\{2,4\\}" file # повторение — тоже
grep "\\(group\\)" file # группировка — тоже
Это неудобно, поэтому большинство админов используют ERE.
ERE — Extended Regular Expressions (флаг -E)
Метасимволы работают без экранирования:
grep -E "error|warning" app.log
grep -E "ab{2,4}" file
grep -E "(group1|group2)" file
egrep — алиас для grep -E, до сих пор работает, но считается deprecated.
PCRE — Perl Compatible Regular Expressions (флаг -P)
Самый мощный диалект с поддержкой lookahead/lookbehind, нежадных квантификаторов, \d, \s, \w:
# Цифры подряд (3 и больше)
grep -P "\d{3,}" file
# IP-адреса (упрощённо)
grep -P "\b(?:\d{1,3}\.){3}\d{1,3}\b" /var/log/auth.log
# Lookahead: error, после которого НЕТ "ignored"
grep -P "error(?!.*ignored)" app.log
PCRE на Ubuntu доступен в стандартной поставке grep — отдельной установки не нужно.
Контекст вокруг совпадения
Когда нужно понять, где в файле произошло событие:
# 3 строки до и после каждого совпадения "ERROR"
grep -C 3 "ERROR" /var/log/app.log
# Только после (для трейсбеков)
grep -A 10 "Traceback" app.log
# Только до (для предыстории ошибки)
grep -B 5 "OOM killer" /var/log/syslog
Рекурсивный поиск
# Все файлы с TODO в проекте
grep -rn "TODO" .
# Только в .py файлах, пропуская .git и venv
grep -rn --include="*.py" --exclude-dir={.git,venv,node_modules} "TODO" .
# Только имена файлов с упоминанием функции
grep -rl "calculate_total" src/
-r идёт по symlinks с осторожностью (раньше было -R, теперь оба ведут себя одинаково в современном grep). Если symlink ведёт в бесконечный цикл — grep автоматически прерывает обход.
grep в конвейерах
Самый частый сценарий — фильтрация вывода других команд:
# Запущенные процессы python
ps aux | grep python
# Только установленные пакеты, относящиеся к nginx
dpkg -l | grep nginx
# Логи systemd-сервиса за последний час
journalctl -u nginx --since "1 hour ago" | grep -i error
# IP-адреса в логах
grep -oE "([0-9]{1,3}\\.){3}[0-9]{1,3}" /var/log/auth.log | sort -u
Классическая ловушка ps aux | grep python — сам grep попадает в результаты. Решение — двойной grep с инвертом:
ps aux | grep python | grep -v grep
Или хитрее — через регулярку, которая не совпадает сама с собой:
ps aux | grep "[p]ython"
[p] — это символьный класс из одной буквы p, совпадает с p. Сам шаблон [p]ython в ps не показан, потому что grep видит свою команду как grep [p]ython, а не python.
Производительность на больших файлах
grep — однопоточный, но очень быстрый: 100-мегабайтный файл сканируется за секунды. Для очень больших файлов (десятки гигабайт) есть оптимизации:
# Указать LANG=C — отключает поддержку Unicode, быстрее на ASCII
LANG=C grep "ERROR" huge.log
# Параллельный grep через GNU parallel
cat huge.log | parallel --pipe --block 10M grep "ERROR"
# ripgrep — альтернатива grep, на порядки быстрее
sudo apt install ripgrep
rg "ERROR" huge.log
ripgrep (rg) — современный заменитель grep: автоматически пропускает .gitignore-исключения, использует PCRE по умолчанию, многопоточный. Для интерактивной работы — лучше grep, для CI-скриптов — оба работают.
Десять рецептов
# 1. Сколько раз слово встретилось в файле
grep -c "TODO" main.py
# 2. Только уникальные совпадения (через sort)
grep -oE "[a-zA-Z_]+@[a-zA-Z.]+" emails.txt | sort -u
# 3. Логи без noise — все ERROR кроме известных
grep "ERROR" app.log | grep -vE "(retry|expected)"
# 4. Найти конфиг с конкретной директивой
sudo grep -rl "server_name" /etc/nginx/
# 5. Поиск кириллицы
grep -P "[А-Яа-я]" mixed.txt
# 6. Количество строк, НЕ начинающихся с #
grep -cv "^#" /etc/nginx/nginx.conf
# 7. Пустые строки — найти и убрать
grep -v "^$" file.txt > file.cleaned.txt
# 8. Большие числа (от 1000)
grep -E "[0-9]{4,}" data.txt
# 9. Сравнение через -f с файлом паттернов
grep -f keywords.txt big-file.txt
# 10. Конкретная функция в кодовой базе
grep -rwn --include="*.go" "func handleLogin" .
Типичные ошибки
Шаблон не в кавычках
grep *.conf file — shell сначала раскроет *.conf в список файлов, и grep увидит уже неожиданные аргументы. Всегда в кавычках: grep "*.conf" file.
Спецсимволы в шаблоне
Точка . в регулярке — это любой символ. Чтобы искать буквальную точку, экранируйте:
# Найдёт "config.yaml", "configXyaml" и так далее
grep "config.yaml" file
# Только буквальную точку
grep "config\\.yaml" file
# или с -F (fixed string — без регулярок)
grep -F "config.yaml" file
-F (или алиас fgrep) — поиск буквальной строки без интерпретации как регулярки. Быстрее, безопаснее, читабельнее для простых случаев.
-E vs PCRE
-E — extended POSIX, -P — Perl. Многие думают, что -E поддерживает \d, \s — нет, это PCRE. Под -E нужно [0-9], [[:space:]].
grep vs ack vs ripgrep
| Утилита | Скорость | Регулярки по умолчанию | Игнорирует .git и .gitignore | Установка |
|---|---|---|---|---|
grep |
базовая | BRE | нет | предустановлен |
egrep (grep -E) |
базовая | ERE | нет | предустановлен |
ack |
медленнее grep | PCRE | да | sudo apt install ack |
ripgrep (rg) |
в 5–10 раз быстрее grep | PCRE | да | sudo apt install ripgrep |
Для скриптов и системных задач — grep (везде есть, стабильный). Для интерактивного поиска по коду — ripgrep (быстрее, умнее с гитом).
Частые вопросы
Чем grep отличается от find?
find ищет файлы по их свойствам (имя, размер, время, права). grep ищет строки внутри файлов. Часто работают в связке: find . -name "*.log" -exec grep "ERROR" {} + — найти ERROR во всех .log-файлах.
Как искать с учётом или без учёта регистра?
По умолчанию grep чувствителен к регистру. Флаг -i отключает это: grep -i "warning" app.log найдёт WARNING, Warning, warning и любую другую комбинацию. Если шаблон должен быть точно с регистром — флаг не используйте.
Как исключить строки, содержащие шаблон?
Флаг -v инвертирует: показывает строки, в которых шаблон не найден. Например, grep -v "^#" config.txt покажет все строки, не начинающиеся с # — то есть весь конфиг без комментариев.
Почему grep не работает с регуляркой типа \d?
\\d — это синтаксис PCRE (Perl). Базовый grep использует POSIX, где нужно [0-9]. Чтобы использовать perl-совместимые регулярки, нужен флаг -P: grep -P "\\d+" file.
Как искать строку, начинающуюся с конкретного слова?
Якорь ^ — начало строки: grep "^ERROR" app.log найдёт строки, начинающиеся точно с ERROR. Аналогично $ для конца строки: grep "completed$" log. Для целого слова (с границами) — флаг -w: grep -w "API" file совпадёт с "API ", " API,", но не с "APIs".
Что запомнить
grepищет строки в файлах или потоке. По умолчанию POSIX BRE.- Шаблон всегда в кавычках, чтобы shell не интерпретировал спецсимволы.
- Топ-флаги:
-i,-v,-n,-r,-l,-c,-A/-B/-Cдля контекста. - Регулярки: BRE (по умолчанию),
-Eдля ERE,-Pдля PCRE. - Рекурсивный поиск:
grep -rn --include "*.py" --exclude-dir .git. - Для конвейеров с пробелами/спецсимволами —
-F(буквальная строка). - На больших файлах —
LANG=C grepилиripgrepдля многопоточности. grep "[p]roc"— трюк, чтобы grep не нашёл сам себя в выводеps.