Com ferramentas GNU:
find . -type f -exec grep -lZ FIND {} + | xargs -r0 grep -l ME
Você pode fazer isso de maneira padrão:
find . -type f -exec grep -q FIND {} \; -exec grep -l ME {} \;
Mas isso executaria dois greps por arquivo. Para evitar a execução de muitos grep
s e ainda ser portável enquanto ainda permite qualquer caractere em nomes de arquivos, você poderia fazer:
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
A ideia é converter a saída de find
em um formato adequado para xargs (que espera uma lista separada em branco (SPC / TAB / NL, e os outros espaços em branco de sua localidade com algumas implementações de xargs
) palavras onde simples, aspas duplas e barras invertidas podem escapar de espaços em branco e um ao outro).
Geralmente, não é possível pós-processar a saída de find -print
, porque ela separa os nomes de arquivos com um caractere de nova linha e não escapa dos caracteres de nova linha encontrados nos nomes dos arquivos. Por exemplo, se vemos:
./a
./b
Não temos como saber se é um arquivo chamado b
em um diretório chamado a<NL>.
ou se são os dois arquivos a
e b
.
Usando .//.
, porque //
não pode aparecer em um caminho de arquivo como saída por find
(porque não existe um diretório com um nome vazio e /
não é permitido em um nome de arquivo ), sabemos que, se vemos uma linha que contém //
, essa é a primeira linha de um novo nome de arquivo. Portanto, podemos usar esse comando awk
para escapar de todos os caracteres de nova linha, mas daqueles que precedem essas linhas.
Se pegarmos o exemplo acima, find
produziria no primeiro caso (um arquivo):
.//a
./b
Qual awk escapa para:
.//a\
./b
Para que xargs
o veja como um argumento. E no segundo caso (dois arquivos):
.//a
.//b
Qual awk
deixaria como está, então xargs
vê dois argumentos.