У меня есть набор 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
)
Но это все равно не сработает для имен файлов, содержащих символы новой строки.
В zsh
(и zsh
только) вы можете сделать:
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" -- "{}"'