За допомогою інструментів 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 та все ще бути портативною, зберігаючи будь-який символ у назвах файлів, ви можете:
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бачить два аргументи.