-exec aumenta as capacidades do comando find e o torna um canivete suíço de forma que podemos usar o resultado da pesquisa para aplicar ações. Então, falaremos sobre o uso da opção -exec para executar comandos e funções de shell, bem como como controlá-los para melhorar a eficácia de sua execução.
find -exec
O comando find é composto de duas partes principais, a expressão e a ação. A expressão é a parte que mais usamos e permite-nos especificar um filtro que define quais arquivos selecionar.
Exemplo, para encontrarmos arquivos terminados em .doc dentro da pasta teste
maria@ti:~$ find teste/ -name *.doc teste/lista.doc
O comando acima não tem somente uma expressão, a ação está só que oculta. Está oculta porque é uma atitude padrão do find. A ação é o comando -print para listar. Tanto faz colocarmos -print:
maria@ti:~$ find teste/ -name *.doc -print teste/lista.doc
-print será executado se nenhuma outra ação for especificada. Ele exibe na tela o resultado da pesquisa colocando cada resultado abaixo do outro. No exemplo acima temos apenas um resultado.
-print só exibe mas a ação -exec nos permite executar comandos nos arquivos encontrados. O resultado é representado por {}. Devemos colocar um \; para dizermos que é o fim das ações.
Vamos listar o tipo de arquivo com o comando file.
maria@ti:~$ find ./teste/ -name "*.txt" -exec file {} \; ./teste/texto.txt: Unicode text, UTF-8 text ./teste/01/revistas.txt: ASCII text ./teste/01/frutas.txt: ASCII text ./teste/02/carros.txt: ASCII text ./teste/nomes.txt: ASCII text
Acima temos:
- file é o comando executado
- {} representa o resultado
- \; é um delimitador.
Qualquer comando que possa ser executado pelo nosso shell é aceitável aqui.
Devemos observar que este não é o nosso shell executando o comando, mas sim o exec do Linux diretamente para executar o comando. Isso significa que qualquer expansão de shell não funcionará aqui, pois não temos um shell. Outro efeito é a indisponibilidade de funções ou aliases do shell.
Usando bash -c
Como solução alternativa para nossas funções de shell ausentes, podemos exportá-las e chamar bash -c com nossa função solicitada em nosso arquivo.
Se tentarmos colocar um texto em todos os arquivos não dará muito certo. Podemos usar bash -c para isso e para deixar mais completo até usar variáveis:
find ./teste/ -name "*.txt" -exec bash -c ' arquivo={} ; echo "Texto de teste" >> $arquivo' \;
Acima, passamos o resultado para uma variável chamada arquivo e usamos echo para passar(>>) um texto para os arquivos dentro de $arquivo.
Se houver espaço em nomes de arquivos então pode colocar aspas
find . -name "*.mp3" -exec bash -c "mp3info \"{}\"" \;
Podemos usar {} muitas vezes:
find . -name "*.mp3" -exec bash -c "basename \"{}\" && file \"{}\" | awk -F: '{\$1=\"\"; print \$0 }'" \;
O Delimitador
Precisamos fornecer ao comando find um delimitador para que ele saiba onde nossos argumentos -exec param. Dois tipos de delimitadores podem ser fornecidos para o argumento -exec: o ponto e vírgula (;) ou o sinal de mais (+).
Como não queremos que nosso shell interprete o ponto e vírgula, precisamos escapar ele com uma barra(\;).
O delimitador determina a maneira como find lida com os resultados da expressão. Se usarmos o ponto e vírgula (;), o comando -exec será repetido para cada resultado separadamente. Por outro lado, se usarmos o sinal de mais (+), todos os resultados das expressões serão concatenados e passados como um todo para o comando -exec, que será executado apenas uma vez.
Vejamos
maria@ti:~$ find ./teste/ -name "*.txt" -exec ls {} \; ./teste/texto.txt ./teste/01/revistas.txt ./teste/01/frutas.txt ./teste/02/carros.txt ./teste/nomes.txt
maria@ti:~$ find ./teste/ -name "*.txt" -exec ls {} + ./teste/01/frutas.txt ./teste/02/carros.txt ./teste/texto.txt ./teste/01/revistas.txt ./teste/nomes.txt