Поиск содержимого командой grep
grep ищет содержимое файлов по шаблону. -r обходит всё дерево каталогов; -n показывает номера строк; -i игнорирует регистр; -l выводит только имена совпавших файлов; --include ограничивает поиск конкретными типами файлов.
find находит файлы по метаданным — имени, размеру, возрасту. Но что если нужно найти файлы по тому, что внутри них? Какой исходный файл вызывает getUserById? Какой конфиг устанавливает MAX_CONNECTIONS? Это работа grep: он читает содержимое файлов строка за строкой и выводит каждую строку, совпадающую с шаблоном.
К концу урока ты будешь искать текстовые шаблоны в целых деревьях каталогов, управлять форматом вывода и использовать базовые регулярные выражения с grep.
После этого урока ты сможешь запускать grep -r для поиска шаблона в дереве каталогов, добавлять -n для номеров строк, -i для поиска без учёта регистра, -l для вывода только имён совпавших файлов и --include для ограничения поиска конкретными расширениями файлов. Ты также сможешь писать простые регулярные выражения для grep.
Базовый grep: шаблон затем файл(ы). grep выводит каждую строку в файле(ах), содержащую шаблон:
grep "error" app.log # строки с "error" в app.log
grep "TODO" src/main.ts # строки с TODO в одном файле
grep "timeout" *.conf # поиск по всем .conf файлам в текущем каталогеШаблон по умолчанию — Базовое Регулярное Выражение (BRE). Для Расширенных Регулярных Выражений (ERE) добавь -E; для Perl-совместимых — -P.
-r выполняет рекурсивный поиск в дереве каталогов. Это флаг, который ты будешь использовать чаще всего в ежедневной работе:
grep -r "getUserById" src/ # поиск по каждому файлу под src/
grep -r "MAX_CONNECTIONS" /etc/ # поиск по всем конфигам под /etcФормат вывода: имя_файла:содержимое_строки. grep следует символическим ссылкам с -r только при использовании -R (заглавная) в GNU grep; -r по умолчанию не следует ссылкам.
-n, -i, -l управляют форматом вывода.
-n— добавляет номер строки перед каждой совпадающей строкой:
grep -rn "panic" ./logs/ # grep -r -n вместе; показывает файл:номерстроки:строка-i— поиск без учёта регистра. НаходитError,error,ERROR:
grep -ri "error" src/-l— выводит только имена файлов, содержащих совпадение (подавляет сами строки). Полезно, когда нужно знать только, какие файлы затронуты:
grep -rl "deprecated" src/ # какие файлы упоминают "deprecated"?Флаги свободно комбинируются: grep -rnil "TODO" . — совершенно допустимо.
--include и --exclude сужают набор файлов. Без них -r ищет по каждому файлу, включая бинарные. Глоб --include сопоставляется с именем файла:
grep -r "import" src/ --include="*.ts" # только TypeScript файлы
grep -r "password" . --include="*.{yml,yaml,json}" # только конфиги
grep -r "debug" . --exclude="*.min.js" # пропустить минифицированный JSГлоб в --include обрабатывается grep, а не оболочкой — бери его в кавычки для надёжности.
Базовые регулярные выражения в grep. Шаблоны grep — не просто строки, по умолчанию это BRE:
| Метасимвол | Значение | Пример |
|---|---|---|
. | любой одиночный символ | gr.p находит grep, grap, gr9p |
* | ноль или более предыдущего | err.*or находит error, err0r, errXXXXor |
^ | начало строки | ^import находит строки, начинающиеся с import |
$ | конец строки | };$ находит строки, заканчивающиеся на }; |
[...] | класс символов | [Ee]rror находит Error или error |
\b | граница слова (GNU) | \berror\b находит error, но не errors |
Используй -F (фиксированная строка), когда не нужна интерпретация регулярных выражений — самый быстрый вариант для поиска обычного текста.
grep -rn "^import " src/ --include="*.ts" # строки, начинающиеся с "import "
grep -rF "process.exit(1)" . # буквальная строка, без regexПоиск всех использований устаревшей функции в TypeScript-проекте.
Нужно найти каждый вызов legacyFetch, чтобы заменить его:
grep -rn "legacyFetch" src/ --include="*.ts"Пример вывода:
src/api/users.ts:14: const data = await legacyFetch('/users');
src/api/posts.ts:8: return legacyFetch('/posts/' + id);
src/utils/http.ts:3: export function legacyFetch(url: string) {Три файла с номерами строк. Теперь выведем только имена файлов для планирования рефакторинга:
grep -rl "legacyFetch" src/ --include="*.ts"src/api/users.ts
src/api/posts.ts
src/utils/http.tsПередадим в wc -l, чтобы подсчитать количество затронутых файлов:
grep -rl "legacyFetch" src/ --include="*.ts" | wc -l3▸Почему это работает
BSD grep (macOS) против GNU grep (Linux). Они разделяют основные флаги (-r, -n, -i, -l), но отличаются в деталях:
--includeи--excludeработают на обеих платформах, но--include-dir— только GNU.-P(Perl-совместимые regex) — только GNU; BSD grep его не поддерживает. Используй-E(ERE) для переносимых расширенных шаблонов.- Цветной вывод (
--color=auto) работает на обеих, но умолчание отличается.
Для скриптов, которые должны работать на macOS, используй только BRE/ERE (-E) и избегай -P. Или установи GNU grep через Homebrew (brew install grep) и вызывай его как ggrep.
▸Частая ошибка
Использование grep -r по большому дереву без --include захватывает бинарные файлы и работает медленно. grep попытается прочитать каждый файл — скомпилированные бинарники, изображения, объекты .git. Добавь --include для ограничения текстовыми расширениями и --exclude-dir=.git для пропуска объектов системы контроля версий:
# Медленно и шумно: бинарные совпадения выводятся как "Binary file X matches"
grep -r "config" .
# Быстро и чисто
grep -r "config" . --include="*.{ts,js,json,yml}" --exclude-dir=.gitНужно найти, какие файлы под src/ содержат слово 'TODO', ища только в .ts-файлах, без вывода совпавших строк — только имена файлов. Какая команда верна?
grep шаблон файл(ы) выводит каждую строку, совпадающую с шаблоном. -r расширяет поиск рекурсивно по дереву каталогов; -n добавляет номера строк; -i делает сопоставление нечувствительным к регистру; -l выводит только имена файлов. --include="*.ext" сужает поиск до конкретных типов файлов — всегда используй с -r, чтобы избежать сканирования бинарников и .git. Шаблоны grep по умолчанию — Базовые Регулярные Выражения; добавь -E для расширенного синтаксиса или -F для поиска буквальной строки без regex.
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.