Заставить grep работать со специальными именами файлов

Заставить grep работать со специальными именами файлов

У меня есть набор txtфайлов, имена которых могут содержать пробелы или специальные символы, например #.

У меня есть grepрешение grep -L "cannot have" $(grep -l "must have" *.txt), как составить список всех файлов, которые имеют must haveрасширение cannot have.

Например, есть файл abc defg.txt, содержащий всего одну строку: must have.

Обычно решение grep должно найти abc defg.txt, но оно возвращает:

grep: abc: No such file or directory
grep: defg.txt: No such file or directory

Я думаю, что для имен файлов, содержащих #, решение grep также недействительно.

Может ли кто-нибудь помочь мне исправить решение grep?

решение1

ЕСЛИесли вы готовы пойти дальше, awk может сделать это за один проход:

awk 'function s(){if(a&&!b){print f}} FNR==1{s();f=FILENAME;a=b=0} 
  /must have/{a=1} /cannot have/{b=1} END{s()}' filepattern

Для более поздней версии gawk вы можете упростить ее с помощью BEGINFILE и ENDFILE. (Как и все ответы awk, вы можете поместить команды awk в файл с помощью -f, и, как и большинство, вы можете легко преобразовать их в perl, если захотите.)

решение2

Поскольку вы уже используете специфичные для GNU параметры ( -L), вы можете сделать следующее:

grep -lZ -- "must have" *.txt | xargs -r0 grep -L -- "cannot have"

Идея состоит в том, чтобы использовать -Zдля печати список имен файлов, разделенных символом NUL, и передать xargs -r0этот список в качестве аргументов второму grep.

Подстановка команд по умолчанию разделяет по пробелу, табуляции и новой строке (и NUL в zsh). Оболочки типа Bourne, отличные от , zshтакже выполняют подстановку для каждого слова, полученного в результате этого разделения.

Вы можете сделать:

IFS='
' # split on newline only
set -f # disable globbing
grep -L -- "cannot have" $(
    set +f # we need globbing for *.txt in this subshell though
    grep -l -- "must have" *.txt
  )

Но это все равно не сработает для имен файлов, содержащих символы новой строки.

В zshzshтолько) вы можете сделать:

IFS=$'\0'
grep -L -- "cannot have" $(grep -lZ -- "must have" *.txt)

Или:

grep -L -- "cannot have" ${(ps:\0:)"$(grep -lZ -- "must have" *.txt)"}

решение3

findВместо этого рассмотрите возможность grepиспользования команды оболочки:

find . -name '*.txt' -print0 | xargs -0 -I{} sh -c 'grep -q "must have" -- "{}" && grep -L "cannot have" -- "{}"'

Связанный контент