Digamos que eu queira executar cmd
em todos *.cpp
os *.hpp
arquivos que contenham a palavra FOO
.
No que diz respeito a encontrar esses arquivos, eu sei que posso fazer,
find /path/to/dir -name '*.[hc]pp' -exec grep -l 'FOO' {} +
mas qual é a maneira correta de estender o processamento para que eu possa executar, digamos, cmd
em cada um desses arquivos?
Eu sei que poderia -exec bash -c '...'
escrever a lógica "se o conteúdo do arquivo contém FOO
, então execute cmd
no arquivo" no arquivo ...
, mas isso parece um canhão para atirar em uma mosca.
-exec … \;
também é um teste, será bem-sucedido se o comando interno retornar o status de saída 0.-exec … +
pode processar muitos nomes de caminho com um comando e sempre é bem-sucedido, portanto, não é um teste útil.grep -q
funciona bem-exec … \;
porque retorna o status de saída 0 quando há uma correspondência (mesmo que um erro tenha sido detectado), 1 caso contrário.Transforme-o
-exec grep … +
em-exec grep -q … \;
para testar os arquivos um por um, para que você possa adicionar outro-exec
que executará condicionalmente o comando desejado:Em geral, o fato
-exec … \;
é que um teste permite criar testes personalizados onde você pode testar praticamente qualquer coisa; especialmente porque você pode executarsh -c
e, portanto, usar pipelines, variáveis que você pode manipular, condicionais de shell e afins para implementar um teste (mas lembre-se: é possível usarfind -exec sh -c
com segurança? ).Com implementações GNU desses utilitários (e um shell com suporte para substituição de processos no estilo ksh¹), você poderia simplesmente fazer:
Acima, com
-r
(também conhecido como--recursive
)grep
fazfind
o trabalho de (cuidado nas versões atuais degrep
, ele se comporta como se-type f
tivesse sido passado para o comando equivalentefind
) e passa al
lista de caminhos de arquivo delimitados por NUL (-Z
) porxargs
meio de um canal cujo caminho é passado para-a
(também conhecido como--arg-file
).Se você ainda quisesse usar
find
(por exemplo, para aplicar critérios de arquivo mais complexos do que o sufixo do nome do arquivo), você faria:(aqui
--
não é necessário porque os caminhos dos arquivos começarão com./
, portanto não-
nem+
).Fazer
find ... -exec grep -q FOO {} \; -exec cmd {} +
funciona, mas significa bifurcar um processo, executargrep
, que envolve carregar e vincular bibliotecas compartilhadas para cada arquivo que será muito mais caro do que o quegrep
precisa ser feito aqui (leia alguns KiBs de texto e encontreFOO
dentro), então é é melhor evitar sempre que possível se o desempenho ou o uso de recursos for uma preocupação.Se você não estiver em um sistema GNU, mas sim seus
find
suportes-print0
exargs
suportes-r
e-0
como será exigido na próxima versão do padrão POSIX, você pode fazer o report de caminhos de arquivos comperl
:Embora isso presuma
cmd
que não lê seu stdin (que aqui, dependendo daxargs
implementação, será aberto em /dev/null ou a extremidade de leitura do canalperl
está gravando, herdada dexargs
).¹ Incluindo implementações de ksh diferentes daquelas baseadas em pdksh e zsh e bash; mude
<(...)
para<{...}
em shells do tipo rc e(...|psub)
emfish