find + xargs: argomento troppo lungo


21

Ho una linea come la seguente:

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

ma ho ricevuto il seguente errore:

xargs: argument line too long

Sono confuso. L'uso di xargssupposto non è di aiuto proprio con questo problema?

Nota: so che posso utilizzare tecnicamente -execin find, ma vorrei capire perché ciò che precede fallisce, poiché la mia comprensione è che xargssi suppone che sappia come dividere l'input in una dimensione gestibile per l'argomento che esegue. Non è vero?

Questo è tutto con zsh.

Risposte:


11

Bene per prima cosa lo -iswitch è deprecato:

-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.

Quindi quando ho cambiato il tuo comando in questo, ha funzionato:

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

Esempio

$ 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 di -I{}

Questo approccio non deve essere utilizzato dall'esecuzione di questo costrutto di comando:

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

trasforma implicitamente questi interruttori a xargs, -xe -L 1. Si -L 1configura in xargsmodo tale da chiamare i comandi per cui si desidera eseguire i file in un unico modo.

Quindi questo sconfigge lo scopo di usare xargsqui poiché se gli dai 1000 file eseguirà il mvcomando 1000 volte.

Quindi quale approccio dovrei usare allora?

Puoi farlo usando xargs in questo modo:

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

O devi solo trovare fare tutto:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +

Grazie! Quando hai detto "This approach shouldn't be used"quale approccio dovrebbe essere usato invece? Sarebbe "find /foot/bar/ -name '*.csv' -print0 | xargs -0 mv -t some_dir'"una soluzione migliore? In tal caso, come fa a xargssapere in questo caso dove nel mvcomando inserire gli argomenti che ottiene dalla pipe? (li posiziona sempre per ultimi?)
Amelio Vazquez-Reina,

@ user815423426 - Farlo con il solo find ... -exec ...è un modo migliore o se vuoi usare anche xargsquello find ... | xargs ... mv -t ...va bene. Sì, li mette sempre per ultimi. Ecco perché quel metodo ha bisogno di -t.
slm

5

L'opzione -iaccetta un argomento facoltativo. Dato che hai inserito uno spazio dopo -i, non c'erano argomenti -isull'opzione e quindi il successivo -0non era un'opzione xargsma il secondo di 6 operandi {} -0 mv -t /some/path {}.

Con solo l'opzione -i, xargs prevedeva un elenco di nomi di file separati da una nuova riga. Dato che probabilmente non c'era una nuova riga nell'input, xargs ha ricevuto quello che sembrava un enorme nome di file (con byte nulli incorporati, ma xargs non lo ha verificato). Questa singola stringa contenente l'intero output di findera più lunga della lunghezza massima della riga di comando, quindi l'errore "riga di comando troppo lunga".

Il tuo comando avrebbe funzionato -i{}invece di -i {}. In alternativa, avresti potuto usare -I {}: -Iè simile a -i, ma accetta un argomento obbligatorio, quindi l'argomento successivo passato a xargsviene usato come argomento -Idell'opzione. Quindi l'argomento successivo -0è interpretato come un'opzione e così via.

Tuttavia, non dovresti usare -I {}affatto. L'uso -Iha tre effetti:

  • -Idisattiva l'elaborazione delle quotazioni, che -0già lo fa.
  • -Icambia la stringa da sostituire, ma {}è il valore predefinito.
  • -Ifa sì che il comando venga eseguito separatamente per ogni record di input, il che è inutile qui poiché il tuo comando ( mv -t) è specificamente progettato per far fronte a più file per invocazione.

O cadere -Ie del -itutto

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

oppure rilascia xargs e usa -exec:

find /foo/bar -name '*.mp4' -exec mv -t /some/path {} +

0

Prova a usare un bash per loop:

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

o se vuoi vedere cosa sta succedendo:

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

0

Se lo vedi mentre usi la conchiglia .
Ciò riguarda il modo in cui il pesce espande la stringa di sostituzione{}

Se si utilizza il pesce, è necessario uscire dalla stringa di sostituzione \{\}

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

oppure usa una stringa di sostituzione diversa

| xargs -I ! echo !
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.