Запустите `grep`, исключая файл по указанному пути

12

Я хочу исключить файл ./test/main.cppиз моего поиска.

Вот что я вижу:

$ grep -r pattern --exclude=./test/main.cpp
./test/main.cpp:pattern
./lib/main.cpp:pattern
./src/main.cpp:pattern

Я знаю, что можно получить желаемый вывод, используя несколько команд в расположении каналов и фильтров, но есть ли какое-либо цитирование / экранирование, которое поможет grepпонять, чего я хочу изначально?

nobar
источник
Решение, основанное на фильтрации выходных данных, плохо масштабируется, поскольку оно без необходимости ищет файл, прежде чем исключить связанные результаты. Проблема усиливается, если я хочу исключить целые каталоги (с --exclude-dir). Вот почему я хотел бы, чтобы grep изначально выполнял исключение.
Нобар
1
--exclude указывает глобус, а не путь
PersianGulf

Ответы:

6

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

find . -type f \! -path './test/main.cpp' -exec grep pattern {} \+

MichalH
источник
Почему вы избежать \!и \+? Кажется, работает нормально без обратной косой черты.
Нобар
@nobar Я привык к этому, потому что некоторые символы являются ключевыми словами оболочки, поэтому вы никогда не будете удивлены, потому что ничего не произойдет, если их экранировать.
MichalH
« grepне могу этого сделать, используйте findвместо этого» - отлично.
Нобар
4

Я не думаю, что это возможно с GNU grep. Тебе не нужны трубы, хотя.

С find:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +

С zsh:

grep pattern ./**/*~./test/main.cpp(.)

(исключает скрытые файлы, точно так же, как исключить .git, .svn ...).

Стефан Шазелас
источник
2

Я мог бы написать книгу: «Утерянное искусство xargs». В find ... -exec … ';запускаете GREP для каждого файла (но вариант с -exec … +не делает). Ну, в наши дни мы тратим процессорные циклы, так почему бы и нет, верно? Но если производительность, память и мощность являются проблемой: используйте xargs:

find . -type f \! -path 'EXCLUDE-FILE' -print0 | xargs -r0 grep 'PATTERN'

ГНУ find«s -print0будет NUL-terminate своих выходных и xargs» -0опционное отличия , что формат в качестве входных данных. Это гарантирует, что любые смешные символы в вашем файле не будут запутаны. -rВариант гарантирует , что нет никакой ошибки в случае findусматривает.

Обратите внимание, теперь вы можете делать такие вещи, как:

find . -type f -print0 | grep -z -v "FILENAME EXCLUDE PATTERN" | 
  xargs -r0 grep 'PATTERN'

GNU grep -zделает то же самое, что и xargs -0.

Otheus
источник
3
Некоторые интересные заметки, но я не уверен, что вы правы по поводу производительности. Как я понимаю, это find -exec (cmd) {} +работает так же, как xargsи find -exec (cmd) {} \;работает так же, как xargs -n1. Другими словами, ваше утверждение верно только в том случае, если используется \;версия.
Нобар
3
Вложение xargsменее эффективно, чем использование -exec … +(хотя и незначительно). Ни один из ответов здесь даже не упомянут -exec … \;.
Жиль "ТАК - перестань быть злым"
1
Ну, с - т. Я встречаюсь с собой Спасибо за комментарии и исправления. Я думал, что \ + была опечатка. О, смотри, -exec ... +добавлено в январе 2005 года. Да, я не устарела ... на ... все.
Отей
2

Если ваша findподдержка -pathбыла добавлена ​​в POSIX в 2008 году, но все еще отсутствует в Solaris:

find . ! -path ./test/main.cpp -type f -exec grep pattern /dev/null {} +
cuonglm
источник
1
Я не думаю, что это сработает, потому что nobar хочет main.cpp в других каталогах
Эрик Ренуф
1
не будет ли ваш шаблон исключать main.cpp из всех остальных каталогов? Это не было бы желательно
Эрик Ренуф
@EricRenouf: О, моя ошибка, неправильное чтение. Обновил мой ответ.
cuonglm
@ Жиль: Почему -pathне POSIX?
cuonglm
Ах, извините, моя ошибка, она была добавлена ​​в 2008 году. Все еще отсутствует в Solaris.
Жиль "ТАК - перестань быть злым"
1

Для записи, вот подход, который я предпочитаю:

grep pattern $(find . -type f ! -path './test/main.cpp')

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


Для меня find -execсинтаксис довольно загадочный. Одной из сложностей find -execявляется (иногда) необходимость экранирования различных символов (особенно если \;используется в Bash). Просто для того, чтобы поместить вещи в привычный контекст, следующие две команды в основном эквивалентны:

find . ! -path ./test/main.cpp -type f -exec grep pattern {} +
find . ! -path ./test/main.cpp -type f -print0 |xargs -0 grep pattern

Если вы хотите исключить подкаталоги , может потребоваться использовать подстановочный знак. Я не совсем понимаю схему здесь - поговорим о тайной :

grep pattern $(find . -type f ! -path './test/main.cpp' ! -path './lib/*' )

Еще одно замечание по обобщению findрешений на основе сценариев для использования в сценариях : grepкомандная строка должна включать параметр -H/ --with-filename. В противном случае это изменит форматирование выходных данных при условии, что в результатах поиска будет только одно имя файла find. Это примечательно, потому что это не кажется необходимым, если использовать grepсобственный поиск файлов (с -rопцией).

... Еще лучше включить /dev/nullпервый файл для поиска. Это решает две проблемы:

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

Итак, окончательный ответ:

grep pattern /dev/null $(find . -type f ! -path './test/main.cpp')
nobar
источник
Вы не должны использовать вывод команды findв подстановке команд. Это прерывается, если есть имена файлов, содержащие пробелы или другие специальные символы. Используйте find -exec, это надежный и простой в использовании.
Жиль "ТАК - перестань быть злым"
@Gilles: Очень хороший момент - также вывод может превышать пределы размера командной строки некоторых программ. Пусть покупатель будет бдителен.
Нобар
Тьфу. Синтаксис «найти» ужасно сложен. «-o» является оператором «или» (также «-or» в Linux), но его типичное использование (например, с «-prune») концептуально не соответствует понятию логического или. Это функционал, а не логика или.
nobar
Другой способ , чтобы исключить подкаталоги , основанные на совпадении имени: find -iname "*target*" -or -name 'exclude' -prune. Ну, это вроде как работает - сокращенный каталог будет указан, но не найден. Если вы не хотите его в список, вы можете добавить свой род излишнего! -name 'exclude'
nobar