Как игнорировать определенные имена файлов, используя «find»?

143

Одна из моих любимых команд BASH:

find . -name '*.*' -exec grep 'SearchString' {} /dev/null \;

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

Однако из-за моего текущего проекта и структуры моей кодовой базы я хотел бы сделать эту команду BASH еще более сложной, не выполняя поиск файлов, которые находятся в каталоге или ниже каталога, содержащего «.svn», или любых файлов, которые конец ".html"

Страница MAN для поиска вроде как меня смутила. Я попытался использовать -prune, и это дало мне странное поведение. В попытке пропустить только HTML-страницы (для начала) я попытался:

find . -wholename './*.html' -prune -exec grep 'SearchString' {} /dev/null \;

и не получил поведение, на которое я надеялся. Я думаю, что я мог упустить смысл -прун. Не могли бы вы, ребята, помочь мне?

Спасибо

Коди С
источник
1
Просто fyi: findэто не встроенная команда bash, а отдельная программа
WakiMiko
1
Вы можете искать внутри файла сgrep -rl 'SearchString'
emanuele
@emanuele Привет, добро пожаловать в SuperUser (и сеть Stack Exchange). Это вопрос, который я задал, и на который был дан ответ 2 с половиной года назад. Как правило, если вы хотите добавить ответ на вопрос, сделайте это путем прокрутки вниз и ответа там, а не в комментарии. Поскольку на этот вопрос уже есть принятый ответ (тот, что помечен зеленой галочкой), маловероятно, что вашему ответу будет уделено много внимания. FYI.
Коди С
1
Привет, это не ответ на ваш вопрос. Как вы указали в преамбуле, это всего лишь совет, который используется findдля поиска внутри файла.
Эммануил
2
FWIW, -name '*.*'не находит все файлы: только те, которые имеют .в своем имени ( *.*обычно это использование DOS-изма, тогда как в Unix вы обычно используете только *для этого). Чтобы действительно сопоставить их все, просто удалите аргумент find . -exec .... Или, если вы хотите применить grep только к файлам (и пропустить каталоги), тогда сделайте find . -type f -exec ....
Стефан

Ответы:

197

Вы можете использовать функцию отрицания (!) В find, чтобы не сопоставлять файлы с конкретными именами:

find . ! -name '*.html' ! -path '*.svn*' -exec grep 'SearchString' {} /dev/null \;

Таким образом, если имя оканчивается на .html или содержит .svn в любом месте пути, оно не будет совпадать, и, следовательно, exec не будет выполнен.

Павел
источник
1
Должен ли я еще указать -name ' . 'где-то там? Буду ли я делать это до или после отрицаний?
Коди С
Было ли намерение вашего *.*совпадения обеспечить соответствие только файлам, содержащим .? Find будет соответствовать всем файлам при отсутствии nameдирективы, поэтому приведенное выше будет соответствовать всем, кроме html и svn
Paul
5
Я думаю, что вы хотите, -wholename '*.svn*'а не -name.
fuenfundachtzig
2
Да, это так, чтобы .svnкаталоги были исключены из результатов поиска.
fuenfundachtzig
1
@Noumenon ! -name '.'следует исключить .из результатов поиска.
Пол
11

У меня была одна и та же проблема в течение длительного времени, и есть несколько решений, которые могут быть применимы в разных ситуациях:

  • ack-grepэто своего рода «разработчик grep», который по умолчанию пропускает каталоги контроля версий и временные файлы. На manстранице объясняется, как искать только определенные типы файлов и как определить свой собственный .
  • grepСобственные --excludeи --exclude-dirопции могут быть очень легко использованы для пропуска файловых глобусов и отдельных каталогов (к сожалению, нет глобализации для каталогов).
  • find . \( -type d -name '.svn' -o -type f -name '*.html' \) -prune -o -print0 | xargs -0 grep ... должно работать, но вышеупомянутые варианты, вероятно, меньше хлопот в долгосрочной перспективе.
l0b0
источник
9

Следующая findкоманда удаляет каталоги, имена которых содержат. .svn Хотя это не происходит в каталоге, сокращенный путь печатается ... ( -name '*.svn'причина!) ..

Вы можете отфильтровать имена каталогов с помощью: grep -d skipкоторый молча пропускает такие входные «имена каталогов».

С GNU grep вы можете использовать -Hвместо /dev/null. Как небольшая побочная проблема: \+может быть намного быстрее, чем \;, например. за 1 миллион однострочных файлов, используя \;потребовалось 4m20s , используя \+потребовалось только 1.2S .

Следующий метод использует xargsвместо -execи предполагает, что ни \nв одном из имен файлов нет новых строк . Как используется здесь, xargsпочти так же, как найти \+.

xargsможет передавать имена файлов, которые содержат последовательные пробелы, изменяя входной разделитель на '\n'с помощью -dопции.

Это исключает каталоги, имена которых содержат .svn и greps только файлы, которые не заканчиваются .html.

find . \( -name '*.svn*' -prune  -o ! -name '*.html' \) |
   xargs -d '\n' grep -Hd skip 'SearchString'
Peter.O
источник
1
Спасибо, что указали на \+вариант действия -exec. Ура для небольших побочных проблем!
Кристиан Лонг,
Конечно, поскольку +это не специальный символ для оболочки, вам не нужно печатать \перед ним.
Скотт