O Exec nos permite passar todos os argumentos de uma só vez {} +
ou passá-los um por um com{} \;
Agora, digamos que eu queira renomear todos os jpeg , não há problema em fazer isso:
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec mv '{}' '{}'.new \;
Mas se eu precisar redirecionar a saída, '{}'
não estará acessível após o redirecionamento.
find . \( -name '*.jpg' -o -name '*.jpeg' \) -exec cjpeg -quality 80 '{}' > optimized_'{}' \;
Isso não funcionaria. Eu precisaria usar um loop for, armazenando a saída do find em uma variável antes de usá-la. Vamos admitir, é complicado.
for f in `find . \( -name '*.jpg' -o -name '*.jpeg' \)`; do cjpeg -quality 80 $f > optimized_$f; done;
Então, existe uma maneira melhor?
>
falta no terceiro exemplo de código?{}
aparecem em cadeias mais longas, pois essas cadeias geralmente não são expandidas.find
, uma vez, e aplicado aofind
próprio comando. O{}
não tem significado especial nesse contexto. O redirecionamento não é um argumentofind
e certamente não faz parte da-exec
cláusula.Respostas:
Você pode usar
bash -c
dentro dofind -exec
comando e usar o parâmetro posicional com o comando bash:Dessa maneira
{}
é fornecida$1
.O
sh
antes do{}
diz ao shell interno seu "nome", a string usada aqui é usada em, por exemplo, mensagens de erro. Isso é discutido mais nesta resposta no stackoverflow .fonte
Você tem uma resposta ( https://unix.stackexchange.com/a/481687/4778 ), mas aqui está o porquê.
O redirecionamento
>
, e também pipes|
, e$
expansão, são todos feitos pelo shell antes que o comando seja executado. Portanto, o stdout é redirecionado paraoptimized_{}
, antes defind
ser iniciado.fonte
O redirecionamento precisa ser citado para evitar que o shell atual o interprete.
Mas citá-lo também evitará que a saída do comando seja redirecionada.
A solução conhecida para isso é chamar um shell:
Nesse caso, o redirecionamento (
>
) é citado no shell atual e funciona corretamente dentro do shell chamado. Ocalled_shell
é usado como o$0
parâmetro (o nome) do shell filho (sh
).Isso funciona bem se um sufixo for adicionado ao nome do arquivo, mas não se você usar um prefixo. Para que um prefixo funcione, é necessário remover o
./
que encontrar prefix para nomes de arquivos${1#./}
e usar a-execdir
opçãoVocê pode (ou não pode) deseja usar a
-iname
opção para que os arquivos com o nome*.JPG
ou*.JpG
ou outras variações também estão incluídos.E você também pode (ou não) chamar o shell uma vez por diretório em vez de uma vez por arquivo adicionando um loop (
for f do … ; done
) e um+
no final:E, finalmente, como
cjpeg
é capaz de gravar diretamente em um arquivo, o redirecionamento pode ser evitado como:fonte
cjpeg
possui uma opção que permite gravar em um arquivo nomeado, em vez da saída padrão. Se a sua versão dofind
suporta a-execdir
opção, você pode tirar vantagem disso para tornar o redirecionamento desnecessário.Nota: na verdade, isso assume a versão BSD de
find
, que parece retirar a liderança./
do nome do arquivo ao expandir para{}
. (Ou, inversamente, o GNUfind
adiciona./
ao nome. Não há um padrão para dizer qual comportamento é "correto".)fonte
find
suporte-execdir
, você pode usá-lo em vez de-exec
. Isso faz com que o comando seja executado no diretório em que o arquivo foi encontrado e{}
se tornará emaa.jpg
vez de./t2/aa.jpg
.cjpeg: can't open optimized_./aa.jpg
.find
e BSDfind
. (Os perigos de utilização de extensões não-padrão.)./
parece ser mais propenso a erros. Uma solução razoável é proposta nesta resposta .Crie um script cjq80:
Torne executável
E use-o em -exec:
fonte