Найти файлы, содержащие строку, и не содержать другой

Скажем, у меня есть некоторые файлы, и я хочу найти файлы среди них, которые содержат строку, но не содержат другую.

grep, основанный на линии, такие условия, как grep -q printf file && grep -vq '#include <stdio.h>' file , не будут работать.

Как мне это сделать?

(Я на Debian, поэтому ответы, специально предназначенные для GNU-версий инструментов, прекрасны.)

4 Solutions collect form web for “Найти файлы, содержащие строку, и не содержать другой”

grep -vl сообщит имя файлов, содержащих по крайней мере одну строку, соответствующую шаблону. Здесь вам нужны файлы, в которых ни одна из строк не соответствует шаблону. GNU grep (как показано на Debian) имеет параметр -L для этого:

 grep -rlZ printf . | xargs -r0 grep -FL '#include <stdio.h>' 

С любым POSIX grep вы можете просто отрицать grep -q :

 find . -type f -exec grep -q printf {} \; \ ! -exec grep -Fq '#include <stdio.h>' {} \; \ -print 

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

Комбинируйте find с bash -c вместо скрипта. Мы берем путь к файлу и сохраняем его в переменной file , а затем передаем его другим командам. Первый grep -q проверяет наличие одного слова / шаблона, который вы хотите. Используя его статус выхода, && передаст его во второй grep -q . Если эта команда не находит совпадения, это означает, что строка не найдена, поэтому, используя ее статус выхода, мы передаем ее для echo через || оператор.

В приведенном ниже примере только file2.txt содержит file2.txt но не слово cadabra .

 $ find -type f -exec bash -c 'file="$@";grep -q "abra" "$file" && grep -q "cadabra" "$file" || echo "$file" ' sh "{}" > ./file2.txt $ ls file1.txt file2.txt file 3.txt $ cat file1.txt abra cadabra $ cat file2.txt abra $ cat file\ 3.txt abra cadabra 

Это довольно легко:

 for fname in ./*.c; do if grep -q -F "printf" "$fname" && ! grep -q -F "#include <stdio.h>" "$fname"; then printf 'File "%s" needs to include stdio.h\n' "$fname" fi done 

Это просмотрит все исходные файлы C в текущем каталоге и сообщит о любом файле, который использует printf() не включая заголовок stdio.h .

Заголовок может быть включен косвенным образом, поэтому, чтобы избежать ложных срабатываний, вы можете передать код через препроцессор C и посмотреть заголовок на предварительно обработанном выходе (это, похоже, работает с gcc и clang ):

 for fname in ./*.c; do if grep -q -F "printf" "$fname" && cc -E "$fname" | ! grep -q "^#.*stdio\.h\""; then printf 'File "%s" needs to include stdio.h\n' "$fname" fi done 

Если я правильно прочитал это требование, вы хотите, чтобы все файлы соответствовали $PAT_INCL минус файлы, соответствующие $PAT_EXCL .

Концептуально это просто заданное вычитание. В unix нет очень хорошей стандартной утилиты для операций set, но comm работает.

 comm -23 <(grep --files-with-match "$PAT_INCL" * | sort) \ <(grep --files-with-match "$PATH_EXCL" * | sort) 

Это можно сделать немного более эффективным, только путем grepping через соответствующие файлы во втором grep:

 # Assuming filenames without whitespace grep --files-with-match "$PAT_INCL" * | sort > incl_files grep --files-with-match "$PAT_EXCL" $(cat incl_files) | sort > excl_files comm -23 incl_files excl_files 
  • grep, как подавить отображение несогласованного файла?
  • Получение только определенных данных на основе имени в текстовом файле
  • Как я могу исключить тип файла * .sql типа файла ack-grep или размер файла больше> 3 МБ?
  • Поиск строк в текстовом файле для шаблона между двумя позициями и печать всей строки
  • Как ответить grep, чтобы выяснить, сколько звонков было рассчитано на время?
  • tr жалоб на «Неверная последовательность байтов»
  • cshell alias: как использовать вложенные одинарные кавычки (') в псевдониме cshell
  • Есть ли способ изменить файл на месте?
  • отфильтровать длину
  • grep для Java-метода
  • Как grep для одиночной цитаты?
  • Linux и Unix - лучшая ОС в мире.