find + xargs: linha de argumento muito longa

17

Eu tenho uma linha como a seguinte:

find /foo/bar -name '*.mp4' -print0 | xargs -i {} -0 mv -t /some/path {}

mas recebi o seguinte erro:

xargs: argument line too long

Estou confuso. O uso de xargs não deve exatamente ajudar com esse problema?

Observação: sei que posso usar, de forma técnica, -exec em find, mas gostaria de entender por que as informações acima falharam, pois meu entendimento é que xargs deveria saber como dividir a entrada em um tamanho gerenciável para o argumento que é executado. Isso não é verdade?

Isto é tudo com zsh.

    
por Amelio Vazquez-Reina 21.07.2013 / 03:42

4 respostas

9

Bem, por um lado, a opção -i está obsoleta:

-i[replace-str]
     This  option  is a synonym for -Ireplace-str if replace-str is specified. 
     If the replace-str argument is missing, the effect is the same as -I{}. 
     This option is deprecated; use -I instead.

Então, quando mudei o seu comando para isso, funcionou:

$ find /foo/bar -name '*.mp4' -print0 | xargs -I{} -0 mv -t /some/path {}

Exemplo

$ find . -print0 | xargs -I{} -0 echo {}
.
./.sshmenu
./The GIT version control system.html
./.vim_SO
./.vim_SO/README.txt
./.vim_SO/.git
./.vim_SO/.git/objects
./.vim_SO/.git/objects/pack
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.idx
./.vim_SO/.git/objects/pack/pack-42dbf7fe4a9b431a51da817ebf58cf69f5b7117b.pack
./.vim_SO/.git/objects/info
./.vim_SO/.git/refs
./.vim_SO/.git/refs/tags
...

Uso de -I{}

Essa abordagem não deve ser usada desde a execução desta construção de comando:

$ find -print0 ... | xargs -I{} -0 ...

ativa implicitamente esses comutadores para xargs , -x e -L 1 . O -L 1 configura xargs para que esteja chamando os comandos para os quais deseja que os arquivos sejam executados de uma única maneira.

Portanto, isso anula o propósito de usar xargs , já que, se você fornecer 1000 arquivos, o comando mv será executado 1000 vezes.

Então qual abordagem devo usar então?

Você pode fazer isso usando xargs como este:

$ find /foot/bar/ -name '*.mp4' -print0 | xargs -0 mv -t /some/path

Ou apenas encontre tudo:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +
    
por 21.07.2013 / 04:23
4

A opção -i aceita um argumento opcional. Como você colocou um espaço depois de -i , não houve argumento para a opção -i e, portanto, o -0 subseqüente não foi uma opção para xargs , mas o segundo de 6 operandos {} -0 mv -t /some/path {} .

Com apenas a opção -i , xargs esperava uma lista separada por novas linhas de nomes de arquivos. Como provavelmente não havia nova linha na entrada, os xargs receberam o que parecia ser um nome de arquivo enorme (com bytes nulos incorporados, mas os xargs não verificaram isso). Esta única string contendo toda a saída de find foi maior que o comprimento máximo da linha de comando, daí o erro “linha de comando muito longa”.

Seu comando teria funcionado com -i{} em vez de -i {} . Como alternativa, você poderia ter usado -I {} : -I é semelhante a -i , mas aceita um argumento obrigatório, portanto, o próximo argumento passado para o xargs é usado como o argumento da opção -I . Então o argumento depois disso é -0 , que é interpretado como uma opção, e assim por diante.

No entanto, você não deve usar -I {} . Usando -I tem três efeitos:

  • -I desativa o processamento de cotações, que -0 já faz.
  • -I altera a sequência a ser substituída, mas {} é o valor padrão.
  • -I faz com que o comando seja executado separadamente para cada registro de entrada, o que é inútil aqui, pois seu comando ( mv -t ) é especificamente destinado a lidar com vários arquivos por chamada.

Copie -I e -i completamente

find /foo/bar -name '*.mp4' -print0 | xargs -0 mv -t /some/path {}

ou solte xargs e use -exec :

find /foo/bar -name '*.mp4' -exec mv -t /some/path {} +
    
por 22.07.2013 / 01:33
0

Tente usar um bash para loop:

for FILE in *.mp4 ; do rm $FILE ; done

ou se você gostaria de ver o que está acontecendo:

for FILE in *.mp4 ; do echo Removing $FILE ; rm $FILE ; done
    
por 19.10.2016 / 15:39
0

Se você está vendo isso enquanto usa o fish shell .
Isto está relacionado a como o peixe expande a string de substituição {}

Se você estiver usando peixes, você precisa escapar da string de substituição \{\}

| xargs -I \{\} echo \{\}

ou use uma string de substituição diferente

| xargs -I ! echo !
    
por 08.03.2018 / 08:21