Есть ли более простой способ grep всех файлов в каталоге?

21

Когда я хочу найти какое-то содержимое во всем дереве, я использую

find . -type f -print0 | xargs -0 grep <search_string>

Есть ли лучший способ сделать это с точки зрения производительности или краткости?

Dancrumb
источник
2
@Downvoter: Рад улучшить этот вопрос, если вы можете поделиться своими проблемами.
Данкрамб
2
во многих версиях find встроен xargs: find. -type f -exec fgrep <search_string> {} +
simpleuser

Ответы:

42

Проверьте, grepподдерживает ли ваш -rвариант поддержки (для рекурса ):

grep -r <search_string> .
Philippos
источник
1
Да ... Я только что нашел stackoverflow.com/questions/16956810/… и это тоже ответ.
Данкрамб
Добавить комментарий о --exclude-dirпроизводительности выступления и у нас есть победитель!
Данкрамб
1
Просто обратите внимание, что это не переносимо, однако grepна последних дистрибутивах FreeBSD и Linux это поддерживается. А почему --exclude-dir? Разве вы не просили обыскать целое дерево ?
Филиппос
Честное замечание ... --exclude-dirна самом деле удобно в моем случае использования (потому что части поддерева большие, но бесполезные), и я спросил о производительности ... но вы правы, в этом нет необходимости.
Данкрамб
В этом случае я должен добавить, что IIRC --exclude-dirявляется эксклюзивным для GNU grep. (-:
Филиппос
13

Субоптимальный ответ: вместо того, чтобы передать результаты findв grep, вы можете просто запустить

find . -type f -exec grep 'research' {} '+'

и вуаля, одна команда вместо двух!

объяснение:

find . -type f

найти все обычные файлы в.

-exec grep 'research'

grep 'исследование'

{}

в найденном имени файла

'+'

используйте одну команду для всех имен файлов, а не один раз для каждого имени файла.

Nb: с ';'этим было бы один раз за имя файла.

Помимо этого, если вы используете это для обработки исходного кода, вы можете посмотреть ack, что сделано для легкого поиска битов кода.

извед

Редактировать :

Вы можете немного расширить это исследование. Во-первых, вы можете использовать -name ''переключатель findдля поиска файлов с указанным шаблоном именования.

Например :

  • только файлы, которые соответствуют журналам: -name '*.log'

  • только файлы, которые соответствуют заголовкам c, но вы не можете использовать прописные или строчные буквы для своих расширений файлов: -iname *.c

Nb: как для grepи ack, -iпереключатель означает, что в этом случае не учитывается регистр.

В этом случае grep будет отображаться без цвета и без номеров строк.

Вы можете изменить это с --colorи -nпереключатели (цвет и линий чисел в файлах , соответственно).

В конце концов, вы можете получить что-то вроде:

find . -name '*.log' -type f -exec grep --color -n 'pattern' {} '+'

например

$ find . -name '*.c' -type f -exec grep -n 'hello' {} '+' 
./test2/target.c:1:hello
Пьер-Антуан Гийом
источник
5
ackявляется большим, и более быстрая версия ackIS ag(серебряный поисковое, geoff.greer.fm/ag )
cfeduke
1
Я предпочитаю это с фильтром, как -name '*.log'это быстрее.
sdkks
@cfeduke Я не пробовал, в основном потому, что ag не является частью репозиториев apt по умолчанию в WSL (ты должен работать с тем, что у тебя есть!)
Пьер-Антуан Гийом
Хитрость заключается в том, чтобы добавить / dev / null в grep, чтобы отобразить имя файла.
ChuckCottrill
Хитрость заключается в поиске только каталогов, а затем -exec grep / dev / null {} / *, чтобы получить все файлы с одним fork / exec на каталог.
ChuckCottrill
12

Если вы хотите перейти в подкаталоги:

grep -R 'pattern' .

Эта -Rопция не является стандартной, но поддерживается большинством распространенных grepреализаций.

Кусалананда
источник
7
Используйте -rвместо -Rпропуска символических ссылок, когда дело касается GNU grep
αғsнιη
1
@AFSHIN Почему бы вам не перейти по символическим ссылкам?
Кусалананда
4
@ Кусалананда рекурсия? Хотя текущие grepреализации GNU ловят рекурсии, я думаю. В противном случае это зависит от того, что вы подразумеваете под «деревом».
Филиппос
2
@Philippos ИМХО, присмотр за пользователем - это не то, что grepдолжен делать инструмент, подобный этому. Если у пользователя есть символьные циклы ссылок в его структуре каталогов, ну, это проблема пользователя :-)
Kusalananda
3
@Kusalananda А если система обеспечит цикл? Никогда не заблудился /sys/devices/cpu/subsystem/devices/cpu/subsystem/devices/cpu/...(-XI, как инструменты, присматривающие за мной (если они не обеспечивают странную магию, которую они называют «ИИ»). (-;
Philippos
5

Как отмечено выше -rили -R(в зависимости от желаемой обработки символической ссылки), это быстрый вариант.

Однако -d <action>может быть полезным в разы.

Приятной особенностью -dявляется команда skip, которая отключает "grep: directory_name: Is a directory", когда вы просто хотите просканировать текущий уровень.

$ grep foo * 
grep: q2: Is a directory 
grep: rt: Is a directory 

$ grep -d skip foo *  
$ 

и конечно:

$ grep -d recurse foo * 
(list of results that don't exist because the word foo isn't in our source code
and I wouldn't publish it anyway).  
$ 

Эта -d skipопция ДЕЙСТВИТЕЛЬНО удобна внутри другого скрипта, поэтому вам не нужно это делать 2> /dev/null. :)

Petro
источник
0

Если вы имеете дело с большим количеством файлов, grep работает быстрее, если вы удаляете файлы, которые необходимо найти, вместо того, чтобы подбирать все файлы в подпапках.

Я использую этот формат иногда:

grep "primary" `find . | grep cpp$`

Найти все файлы в подпапках .этого конца вcpp . Затем grep эти файлы для "основного".

Если вы хотите, вы можете продолжать передавать эти результаты в дальнейшие вызовы grep:

grep "primary" `find . | grep cpp$` | grep -v "ignoreThis" | grep -i "caseInsensitiveGrep"
Rudy
источник
1
Backtics не являются хорошей современной практикой, они почти устарели
Кристофер
1
Это сломается, если у вас есть файлы со специальными символами в именах. Я не знаю, насколько они должны быть особенными, чтобы быть слишком особенными для того, чтобы это работало как есть, но то, что вы делаете, - это почти то же самое, что парсинг вывода ls, что тоже плохо.
CVn