Como uso um pipe no parâmetro exec para um comando find?
Pergunta
Estou tentando construir um comando find para processar vários arquivos em um diretório usando dois executáveis diferentes.Infelizmente, -exec
no find não permite usar pipe ou mesmo \|
porque o shell interpreta esse caractere primeiro.
Aqui está especificamente o que estou tentando fazer (o que não funciona porque o pipe termina o comando find):
find /path/to/jpgs -type f -exec jhead -v {} | grep 123 \; -print
Solução
Experimente isso
find /path/to/jpgs -type f -exec sh -c 'jhead -v {} | grep 123' \; -print
Alternativamente, você pode tentar incorporar sua instrução exec dentro de um script sh e então fazer:
find -exec some_script {} \;
Outras dicas
Uma abordagem um pouco diferente seria usar xargs:
find /path/to/jpgs -type f -print0 | xargs -0 jhead -v | grep 123
que sempre achei um pouco mais fácil de entender e adaptar (os argumentos -print0 e -0 são necessários para lidar com nomes de arquivos contendo espaços em branco)
Esse poder (não testado) será mais eficaz do que usar -exec porque canalizará a lista de arquivos para xargs e xargs garante que a linha de comando do jhead não fique muito longa.
Com -exec
você só pode executar um único executável com alguns argumentos, e não comandos shell arbitrários.Para contornar isso, você pode usar sh -c '<shell command>'
.
Observe que o uso de -exec
é bastante ineficiente.Para cada arquivo encontrado, o comando deve ser executado novamente.Seria mais eficiente se você pudesse evitar isso.(Por exemplo, movendo o grep
fora da -exec
ou canalizando os resultados de find
para xargs
como sugerido por palmito.)
Usando find
comando para este tipo de tarefa talvez não seja a melhor alternativa.Eu uso o seguinte comando frequentemente para encontrar arquivos que contenham as informações solicitadas:
for i in dist/*.jar; do echo ">> $i"; jar -tf "$i" | grep BeanException; done
Como isso gera uma lista, você não:
find /path/to/jpgs -type f -exec jhead -v {} \; | grep 123
ou
find /path/to/jpgs -type f -print -exec jhead -v {} \; | grep 123
Coloque seu grep nos resultados de find -exec.
Existe outra maneira de fazer isso, mas também é um belo gueto.
Usando a opção shell extquote você pode fazer algo semelhante a isso para encontrar coisas exec e depois canalizá-las para sh.
root@ifrit findtest # find -type f -exec echo ls $"|" cat \;|sh
filename
root@ifrit findtest # find -type f -exec echo ls $"|" cat $"|" xargs cat\;|sh
h
Eu apenas pensei em acrescentar isso porque, pelo menos da maneira como visualizei, estava mais próximo da questão original do OP de usar pipes dentro do exec.