Como esse comando find usando “find… -exec sh -c '…' sh {} +” funciona?

8

O @StephaneChazelas postou a seguinte solução para estas perguntas e respostas: Estou com problemas para usar o "find -exec {} +" .

$ find . -iname "*.extension" -exec sh -c '
  exec <command> "$@" <additional parameters>' sh {} +

O que exatamente está acontecendo aqui? Especificamente, o que o último sh {}faz? Parece que existe apenas para pacificar o -execcomando do find, para que ele tenha algo a fazer, um NOOP.

Eu poderia facilmente colocar echo {}lá e parece funcionar muito bem.

slm
fonte

Respostas:

9

A sintaxe é:

find ... -exec cmd {} +

findencontrará vários arquivos com base nos critérios ...e será executado cmdcom essa lista de caminhos de arquivos como argumentos, o maior número possível, sem ultrapassar o limite no tamanho dos argumentos de um comando.

Se necessário, pode dividir a lista de arquivos e ligar cmdvárias vezes. Por exemplo, pode acabar chamando:

cmd ./file1 ./file2 ... ./file3000
cmd ./file3001 ./file3002 ... ./file4321

Uma limitação disso é que {}deve ser a última. Você não pode, por exemplo, escrever:

find ... -exec cmd {} other args +

como você poderia com em ';'vez de '+'.

Você pode escrever:

find ... -exec echo foo {} +

mas não:

find ... -exec echo {} foo +

Portanto, se você precisar adicionar alguns argumentos extras para cmddepois da lista de arquivos, precisará recorrer à chamada de shell. (Outras razões pelas quais você precisaria chamar um shell seria sempre que precisar usar um recurso de shell, como redirecionamentos, canais, algumas expansões de cadeia ...)

Em sh -c 'inline-script' x a b c, para o inline-script, $0é x, $1é a, $2é b... assim "$@"como a lista desses 3 argumentos: a, bec. Então em:

find ... -exec sh -c 'cmd "$@" other arg' find-sh {} +

Para o script embutido , $0(que é usado, por exemplo, ao exibir mensagens de erro), é definido como find-she "$@"é a lista de arquivos (o que se findexpande {}).

Usando o execbuiltin especial do shell:

find ... -exec sh -c 'exec cmd "$@" other arg' find-sh {} +

Dizemos ao shell para não bifurcar um processo extra para executar cmd, mas para executá-lo no mesmo processo (substituindo o processo do shell em execução por esse comando). Alguns escudos como bash, zshe algumas implementações de kshfazer isso de forma implícita para o último comando em um script.

Stéphane Chazelas
fonte
Você poderia usar um subshell em vez de exec lá? -exec sh -c '(cmd1; cmd2;)' find-sh {} +?
slm
Então, se eu entendi direito, find-sh {}existem argumentos para o comando sh -c '...', certo?
slm
@slm, findvai chamar /bin/shcom como argumentos ("sh", "-c", "...", "find-sh", "./file1", "./file2"...). And inside ... , that maps (the shells maps that) to $ 0` ser "find-sh", e os parâmetros de posição ( $1, $2... o que você poderia dizer que são os argumentos para o script embutido ), sendo ./file1, ./file2.
Stéphane Chazelas