С инструментами GNU:
find . -type f -exec grep -lZ FIND {} + | xargs -r0 grep -l ME
Вы можете сделать стандартно:
find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;
Но это будет запускать два greps на файл. Чтобы избежать запуска такого количества grep
s и при этом быть переносимым, при этом допуская любой символ в именах файлов, вы можете сделать:
convert_to_xargs() {
sed "s/[[:blank:]\"\']/\\\\&/g" | awk '
{
if (NR > 1) {
printf "%s", line
if (!index($0, "//")) printf "\\"
print ""
}
line = $0
}'
END { print line }'
}
find .//. -type f |
convert_to_xargs |
xargs grep -l FIND |
convert_to_xargs |
xargs grep -l ME
Идея состоит в том, чтобы преобразовать вывод find
в формат, подходящий для xargs (который ожидает пробел (SPC / TAB / NL и другие пробелы в вашей локали с некоторыми реализациями xargs
), разделенного списка слов, где одинарные, двойные кавычки и обратные слеши могут избегать пробелов и друг друга).
Как правило, вы не можете постобработать вывод find -print
, потому что он разделяет имена файлов символом новой строки и не экранирует символы новой строки, которые встречаются в именах файлов. Например, если мы видим:
./a
./b
У нас нет никакой возможности узнать, называется ли это один файл b
в каталоге a<NL>.
или это два файла a
и b
.
Используя .//.
, потому что //
иначе не может появиться в пути к файлу как вывод find
(потому что не существует такой вещи, как каталог с пустым именем и /
не допускается в имени файла), мы знаем, что если мы увидим строку, которая содержит //
, то это первая строка нового имени файла. Таким образом, мы можем использовать эту awk
команду для экранирования всех символов новой строки, кроме тех, которые предшествуют этим строкам.
Если мы возьмем приведенный выше пример, find
будет выводиться в первом случае (один файл):
.//a
./b
Какой awk уходит в:
.//a\
./b
Так что xargs
это воспринимается как один аргумент. И во втором случае (два файла):
.//a
.//b
Который awk
оставил бы как есть, так xargs
видит два аргумента.