find + xargs: linha de argumento muito longa

21

Eu tenho uma linha como a seguinte:

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

mas eu recebi o seguinte erro:

xargs: argument line too long

Estou confuso. O uso de xargssupostamente não ajuda precisamente com esse problema?

Nota: Eu sei que posso usar tecnicamente -execno find, mas gostaria de entender por que o acima falhou, pois meu entendimento é que xargsé necessário saber como dividir a entrada em um tamanho gerenciável para o argumento executado. Isso não é verdade?

Isso é tudo com o zsh.

Amelio Vazquez-Reina
fonte

Respostas:

11

Bem, por um lado, o -iswitch foi descontinuado:

-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 eu mudei 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 deste comando:

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

Acontece implicitamente nesses muda para xargs, -xe -L 1. Ele é -L 1configurado xargspara chamar os comandos pelos quais você deseja executar os arquivos de uma única maneira.

Portanto, isso anula o propósito de usar xargsaqui, pois se você fornecer 1000 arquivos, ele executará o mvcomando 1000 vezes.

Então, qual abordagem devo usar então?

Você pode fazer isso usando xargs assim:

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

Ou apenas encontre fazer tudo:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +
slm
fonte
Obrigado! Quando você disse "This approach shouldn't be used"que abordagem deveria ser usada, então? Seria "find /foot/bar/ -name '*.csv' -print0 | xargs -0 mv -t some_dir'"uma solução melhor? Se sim, como é xargsque, nesse caso, onde está o mvcomando para alimentar os argumentos que obtém do pipe? (sempre os coloca por último?) #
Amelio Vazquez-Reina
@ user815423426 - Fazê-lo com apenas a find ... -exec ...uma maneira melhor ou se você quiser usar xargso find ... | xargs ... mv -t ...é muito fina. Sim, sempre os coloca por último. É por isso que esse método precisa do -t.
Slm
5

A opção -iaceita um argumento opcional. Como você colocou um espaço depois -i, não houve argumento para a -iopção e, portanto, o subsequente -0não era uma opção, xargsmas o segundo dos 6 operandos {} -0 mv -t /some/path {}.

Com apenas a opção -i, xargs esperava uma lista separada por nova linha de nomes de arquivos. Como provavelmente não havia nova linha na entrada, o xargs recebeu o que parecia ser um nome de arquivo enorme (com bytes nulos incorporados, mas o xargs não verificou isso). Essa sequência única contendo toda a saída de findera maior que o comprimento máximo da linha de comando, daí o erro “linha de comando muito longa”.

Seu comando teria trabalhado com em -i{}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 -Iopção. Em seguida, o argumento a seguir é o -0que é interpretado como uma opção e assim por diante.

No entanto, você não deve usar -I {}nada. O uso -Item três efeitos:

  • -Idesativa o processamento de cotação, o que -0já faz.
  • -Ialtera a cadeia para substituir, mas {}é o valor padrão.
  • -Ifaz com que o comando seja executado separadamente para cada registro de entrada, o que é inútil aqui, pois seu comando ( mv -t) se destina especificamente a lidar com vários arquivos por chamada.

Deixar cair -Ie -icompletamente

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 {} +
Gilles 'SO- parar de ser mau'
fonte
0

Tente usar um loop bash para:

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

ou se você quiser ver o que está acontecendo:

for FILE in *.mp4 ; do echo Removing $FILE ; rm $FILE ; done
C. Shamis
fonte
0

Se você estiver vendo isso enquanto estiver usando a casca do peixe .
Isso se refere a como o peixe expande a cadeia de substituição{}

Se você estiver usando peixe, precisará escapar da corda de substituição \{\}

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

ou use uma sequência de substituição diferente

| xargs -I ! echo !
nelaaro
fonte