-bash: /usr/bin/mv: Argumentliste zu lang

-bash: /usr/bin/mv: Argumentliste zu lang

Wenn ich diesen Befehl verwende mv * .., erhalte ich den Fehler -bash: /usr/bin/mv: Argument list too long. Ich verstehe, dass der Grund dafür darin liegt, dass Bash das Sternchen tatsächlich auf jede passende Datei erweitert, wodurch eine sehr lange Befehlszeile entsteht. Wie kann ich diesen Fehler beheben?

Antwort1

Die Begrenzung liegt nicht in Bash, sondern im execve()Systemaufruf, der zum Ausführen externer Befehle verwendet wird. Sie könnten Folgendes tun:

printf '%s\0' * | xargs -r0 mv -t .. --

Da printfin eingebaut ist bash, gibt es execve()an dieser Stelle kein . Und es ist seine xargsAufgabe, die Liste der Argumente aufzuteilen, um diese Begrenzung zu vermeiden. Hier wird die GNU-spezifische -tOption von GNU verwendet mv.

Mit zshkönnen Sie das mvintegrierte Programm laden:

zmodload zsh/files
mv -- * ..

Oder verwenden Sie den zargsHelfer zum Aufteilen:

autoload -Uz zargs # best in ~/.zshrc
zargs -r -- ./* -- mv -t ..

Sie können es ./*durch ersetzen ./*(D), um auch versteckte Dateien zu verschieben, oder den oNGlob-Qualifizierer hinzufügen, um die Sortierung der Dateinamen zu überspringen, oder den NGlob-Qualifizierer (mit zargs -r), um den Fehler zu vermeiden, wenn keine passende Datei vorhanden ist.

zargs -r -- ./*(ND) -- mv -t ..

Gleich wie:

print -rNC1 ./*(ND) | xargs -r0 mv -t ..

Aber ohne die Abhängigkeit zu GNU xargs.

Unter Linux (dem Kernel, der normalerweise unter Ubuntu verwendet wird) können Sie dieses execve()Limit auch erhöhen, indem Sie das stacksizeRessourcenlimit erhöhen:

bash-5.1$ /bin/true {1..200000}
bash: /bin/true: Argument list too long
bash-5.1$ ulimit -s unlimited
bash-5.1$ /bin/true {1..200000}
bash-5.1$ 

Es ist nicht völlig unbegrenzt (zumindest nicht in aktuellen Versionen des Kernels):

bash-5.1$ /bin/true {1..2000000}
bash: /bin/true: Argument list too long

Beachten Sie, dass die Begrenzung auf der kumulierten Größe der Argumente und der an übergebenen Umgebungsvariablen liegt execve(), die Berechnung jedoch nicht nur aus der Summe der darin enthaltenen Bytes besteht und die Vorgehensweise je nach Betriebssystem und Version unterschiedlich ist.

Antwort2

Sie können es in zwei oder mehr Schritten tun:

mv [a-k]* ..    # or some other pattern matching a subset of the files
mv -- * ..

Oder in einer Schleife,

for name in *; do
    mv -- "$name" ..
done

(Dazu müsste allerdings mvjeder einzelne Name einzeln behandelt werden.)

Oder lass finddir helfen:

find . -mindepth 1 -maxdepth 1 -exec mv -t .. -- {} +

Dadurch werden alle Namen im aktuellen Verzeichnis gefunden und mithilfe von GNU mit möglichst mvwenigen Aufrufen von in das darüber liegende Verzeichnis verschoben .mv

Ohne GNU mv, aber mit einem find, das immer noch die Nicht-Standard- -mindepthund -maxdepthPrädikate kennt,

find . -mindepth 1 -maxdepth 1 -exec sh -c 'mv -- "$@" ..' sh {} +

Keine dieser Varianten kümmert sich um Namenskollisionen. Sie sollten mit Daten testen, die ordnungsgemäß gesichert sind.

verwandte Informationen