find + xargs: Argumentzeile zu lang

21

Ich habe eine Zeile wie die folgende:

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

aber ich habe folgenden fehler bekommen:

xargs: argument line too long

Ich bin verwirrt. Soll die Verwendung von nicht xargsgenau bei diesem Problem helfen?

Hinweis: Ich weiß, dass ich technisch -execin find verwenden kann, aber ich möchte verstehen, warum das oben Gesagte fehlschlägt, da ich verstehen möchte, wie xargsdie Eingabe in eine handhabbare Größe für das Argument aufgeteilt werden kann, das sie ausführt. Ist das nicht wahr?

Das ist alles mit zsh.

Amelio Vazquez-Reina
quelle

Antworten:

11

Nun, zum einen ist der -iSchalter veraltet:

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

Als ich Ihren Befehl in diesen änderte, funktionierte es:

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

Beispiel

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

Gebrauch von -I{}

Dieser Ansatz sollte nicht verwendet werden, da dieses Befehlskonstrukt ausgeführt wird:

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

implizit schaltet dieser Schalter auf xargs, -xund -L 1. Das -L 1konfiguriert xargsso, dass es die Befehle aufruft, über die die Dateien auf eine einzige Weise ausgeführt werden sollen.

Dies macht den Zweck der Verwendung dieses Befehls xargszunichte, da der mvBefehl 1000-mal ausgeführt wird, wenn Sie 1000 Dateien angeben .

Welchen Ansatz soll ich dann verwenden?

Du kannst es mit xargs wie folgt machen:

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

Oder einfach alles finden lassen:

$ find /foot/bar/ -name '*.mp4' -exec mv -t /some/path {} +
slm
quelle
Vielen Dank! Wann haben Sie gesagt, "This approach shouldn't be used"welcher Ansatz sollte stattdessen verwendet werden? Wäre "find /foot/bar/ -name '*.csv' -print0 | xargs -0 mv -t some_dir'"eine bessere Lösung? Wenn ja, wie ist xargsin diesem Fall wissen , wo in der mvBefehl - Feed in den Argumenten aus dem Rohr kommt? (Platziert es sie immer zuletzt?)
Amelio Vazquez-Reina
@ user815423426 - es mit nur das tut find ... -exec ...ein besserer Weg, oder wenn Sie verwenden mögen , xargsdas find ... | xargs ... mv -t ...ist auch in Ordnung. Ja, es bringt sie immer an die letzte Stelle. Deshalb braucht diese Methode die -t.
SLM
5

Die Option -iakzeptiert ein optionales Argument. Da Sie nach ein Leerzeichen setzen -i, gab es kein Argument für die -iOption und daher war das folgende -0keine Option für xargsden zweiten von 6 Operanden {} -0 mv -t /some/path {}.

Mit nur der Option -ierwartete xargs eine durch Zeilenumbrüche getrennte Liste von Dateinamen. Da die Eingabe wahrscheinlich keine neue Zeile enthielt, erhielt xargs einen riesigen Dateinamen (mit eingebetteten Null-Bytes, aber xargs überprüfte das nicht). Diese einzelne Zeichenfolge, die die gesamte Ausgabe von enthält, findwar länger als die maximale Befehlszeilenlänge, daher der Fehler "Befehlszeile zu lang".

Ihr Befehl hätte -i{}statt mit gearbeitet -i {}. Alternativ hätten Sie auch Folgendes verwenden können -I {}: -Iist ähnlich -i, verwendet jedoch ein obligatorisches Argument, sodass das nächste Argument, das an übergeben xargswird, als Argument der -IOption verwendet wird. Dann wird das folgende Argument -0als Option interpretiert und so weiter.

Sie sollten jedoch überhaupt nicht verwenden -I {}. Verwenden -Ihat drei Effekte:

  • -Ideaktiviert die Angebotsverarbeitung, die dies -0bereits tut.
  • -IÄndert die zu ersetzende Zeichenfolge, ist jedoch {}der Standardwert.
  • -IBewirkt, dass der Befehl für jeden Eingabedatensatz separat ausgeführt wird. Dies ist hier unbrauchbar, da Ihr Befehl ( mv -t) speziell dafür vorgesehen ist, mit mehreren Dateien pro Aufruf umzugehen.

Entweder fallen lassen -Iund -iganz

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

oder lösche xargs und benutze -exec:

find /foo/bar -name '*.mp4' -exec mv -t /some/path {} +
Gilles 'SO - hör auf böse zu sein'
quelle
0

Versuchen Sie es mit einer Bash for-Schleife:

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

oder wenn Sie sehen möchten, was los ist:

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

Wenn Sie dies sehen, während Sie die Fischschale verwenden .
Dies bezieht sich darauf, wie Fische die Ersatzschnur ausdehnen{}

Wenn Sie Fisch verwenden, müssen Sie die Ersatzschnur entkommen \{\}

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

oder verwenden Sie eine andere Ersatzzeichenfolge

| xargs -I ! echo !
Nelaaro
quelle