
Я хочу выполнить grep для файлов, содержащих шаблон A (iwant), но я хочу исключить файлы, содержащие шаблон B (idontwant).
Пример:
read -p "...what are you looking for: " iwant
read -p "...what should not be included: " idontwant
iwant="blue car"
idontwant="red car"
Предположим, у меня есть следующие файлы:
-rw-rw-r--. 1 terpentin terpentin 45 Jun 8 16:04 blue.car
-rw-rw-r--. 1 terpentin terpentin 44 Jun 8 16:05 mixed.car
-rw-rw-r--. 1 terpentin terpentin 40 Jun 8 16:04 red.car
find . -type f -print -exec cat {} \;
./mixed.car
blue car
red car
blue car
./red.car
red car
red car
red car
./blue.car
blue car
blue car
blue car
Как можно получить в результате только файл «./blue.car»?
Исходный контент включает сотни длинных текстовых файлов, поэтому важно обеспечить максимальную эффективность использования ресурсов.
решение1
Использовать
find . -type f ! -exec grep -q "$idontwant" {} ';' -exec grep -q "$iwant" {} ';' -print
или
find . -type f -exec grep -q "$iwant" {} ';' ! -exec grep -q "$idontwant" {} ';' -print
- Термины (иногда называемые «предикатами») в
find
команде характеризуются кактесты(например,-type f
) идействия(например,-print
и-delete
). Из страницы руководства может быть трудно понять, что-exec
является и действиеи а тест. Так же, как инайти . -тип f -mtime -30 -имя '*.txt' -читаемый -размер +5тест 6 тест 7 тест 8…
последовательно сужает поиск до файлов, которые соответствуют всем критериям (удовлетворяют всем указанным тестам), поэтомунайти . -execкоманда 1{} ';' -execкоманда 2{} ';' -execкоманда 3{} ';' …
находит файлы, для которых все команды выполнены успешно. - Любой
find
тест можно отменить (инвертировать), поставив перед ним!
. Такfind . ! -type d
находятся простые файлы, символические ссылки, именованные каналы, сокеты и файлы устройств — все, кроме каталогов. - Обратите внимание, что это
! -exec grep …
не эквивалентно-exec grep -v …
.-exec grep -v …
будут найдены файлы, в которых есть хотя бы одна несоответствующая строка.! -exec grep …
будут найдены файлы, в которыхнетлинии совпадают. - Параметр
-q
togrep
официально является синонимом--quiet
, но он также означаетбыстрый. Он не выводит никаких данных (за исключением, возможно, сообщений об ошибках, если применимо), но также он завершает работу, как только находит совпадение — он не читает каждый файл до конца, чтобы найтикаждыйсовпадение. (Конечно, если файл не содержит совпадений, тоgrep
для определения этого необходимо прочитать его полностью.) - Так (TL;DR) команды находят файлы, для которых
grep -q "$iwant" файл
преуспевает иgrep -q "$idontwant"файл
не выполняется (потому что мы добавили перед ним!
). - Две команды функционально эквивалентны, но могут иметь разную производительность (т. е. могут занимать разное время для выполнения). Если только несколько файлов содержат строки поиска,
найти . -тип f -exec grep -q "$iwant" {} ';' ! -exec grep -q "$idontwant" {} ';' –print
будет быстрее, потому чтоgrep "$iwant"
устранит большинство файлов. Если многие файлы содержат обе строки, тонайти . -тип f ! -exec grep -q "$idontwant" {} ';' -exec grep -q "$iwant" {} ';' –print
будет быстрее, так как! grep "$idontwant"
будет удалена большая часть файлов.
решение2
С помощью этой команды GNU grep
мы можем выполнить извлечение имени файла, разумно выбрав параметры регулярных выражений и grep:
$ grep -lzPsr '(?s:(?=.*blue)(?!.*red))' .
Мы запускаем grep в режиме slurp (-z), в котором весь файл рассматривается как одна большая строка.
Параметр -l выведет список имен файлов, соответствующих регулярному выражению.
Параметр -r будет рекурсивно выполняться для всех файлов в текущем каталоге и ниже.
Параметр -s отключит grep и не будет выдавать никаких предупреждений.
Регулярное выражение будет искать наличие синего цвета и отсутствие красного цвета в файле, чтобы ответить «да».
Параметр -P вызывает механизм регулярных выражений Perl в grep, что позволяет нам воспользоваться преимуществами регулярных выражений pcre.