За допомогою інструментів GNU:
find . -type f -exec grep -lZ FIND {} + | xargs -r0 grep -l ME
Ви можете робити стандартно:
find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;
Але це запустило б два грепи на файл. Щоб уникнути запуску такої кількості 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
Який бук втікає:
.//a\
./b
Тож це xargs
розглядає це як один аргумент. А у другому випадку (два файли):
.//a
.//b
Що awk
залишається таким, як є, так xargs
бачить два аргументи.