„mv: Argumentliste zu lang“ lösen?

„mv: Argumentliste zu lang“ lösen?

Ich habe einen Ordner mit mehr als einer Million Dateien, der sortiert werden muss, aber ich kann nichts tun, weil mvdiese Meldung ständig ausgegeben wird

-bash: /bin/mv: Argument list too long

Ich verwende diesen Befehl, um Dateien ohne Erweiterung zu verschieben:

mv -- !(*.jpg|*.png|*.bmp) targetdir/

Antwort1

xargsist das richtige Werkzeug für diese Aufgabe. Das oderfindmit -exec … {} +. Diese Tools führen einen Befehl mehrmals aus, mit so vielen Argumenten, wie auf einmal übergeben werden können.

Beide Methoden sind einfacher auszuführen, wenn die Liste der variablen Argumente am Ende steht, was hier nicht der Fall ist: Das letzte Argument to mvist das Ziel. Bei GNU-Dienstprogrammen (also unter nicht eingebettetem Linux oder Cygwin) ist die -tOption to mvnützlich, um das Ziel zuerst zu übergeben.

Wenn die Dateinamen weder Leerzeichen noch Ähnliches enthalten \"'und nicht mit -¹ beginnen, können Sie die Dateinamen einfach als Eingabe für angeben xargs(der echoBefehl ist in Bash integriert und unterliegt daher nicht der Längenbeschränkung für Befehlszeilen; wenn Sie sehen !: event not found, müssen Sie die Globbing-Syntax mit aktivieren shopt -s extglob):

echo !(*.jpg|*.png|*.bmp) | xargs mv -t targetdir --

-0Sie können die Option verwenden , um xargsanstelle des standardmäßigen Anführungszeichenformats eine durch Nullen getrennte Eingabe zu verwenden.

printf '%s\0' !(*.jpg|*.png|*.bmp) | xargs -0 mv -t targetdir --

Alternativ können Sie die Liste der Dateinamen auch mit erzeugen find. Um eine Rekursion in Unterverzeichnisse zu vermeiden, verwenden Sie -type d -prune. Da für die aufgelisteten Bilddateien keine Aktion angegeben ist, werden nur die anderen Dateien verschoben.

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec mv -t targetdir/ {} +

(Im Gegensatz zu den Platzhaltermethoden der Shell schließt dies Punktdateien ein.)

Wenn Sie keine GNU-Dienstprogramme haben, können Sie eine Zwischenshell verwenden, um die Argumente in die richtige Reihenfolge zu bringen. Diese Methode funktioniert auf allen POSIX-Systemen.

find . -name . -o -type d -prune -o \
       -name '*.jpg' -o -name '*.png' -o -name '*.bmp' -o \
       -exec sh -c 'mv "$@" "$0"' targetdir/ {} +

In zsh können Sie diemveingebaut:

setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/

oder wenn Sie es vorziehen, mvandere Namen weiterhin auf die externen Befehle verweisen zu lassen:

setopt extended_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- ^*.(jpg|png|bmp) targetdir/

oder mit Globs im KSH-Stil:

setopt ksh_glob
zmodload -Fm zsh/files b:zf_\*
zf_mv -- !(*.jpg|*.png|*.bmp) targetdir/

Alternativ können Sie GNU mvundzargs:

autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/ --

¹ Bei einigen xargsImplementierungen müssen Dateinamen auch gültiger Text in der aktuellen Sprache sein. Einige würden einen Dateinamen auch _als Hinweis auf das Ende der Eingabe betrachten (kann mit vermieden werden -E '')

Antwort2

Wenn die Arbeit mit dem Linux-Kernel ausreicht, können Sie einfach

ulimit -S -s unlimited

Das wird funktionieren, weil der Linux-Kernel vor etwa 10 Jahren einen Patch enthielt, der das Argumentlimit so geändert hat, dass es auf der Stapelgröße basiert:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b6a2fea39318e43fee84fa7b0b90d68bed92d2ba

Wenn Sie keinen unbegrenzten Stapelspeicherplatz wünschen, können Sie beispielsweise Folgendes sagen:

ulimit -S -s 100000

um den Stapel auf 100 MB zu begrenzen. Beachten Sie, dass Sie den Stapelspeicherplatz auf die normale Stapelnutzung (normalerweise 8 MB) plus die Größe der Befehlszeile einstellen müssen, die Sie verwenden möchten.

Sie können das tatsächliche Limit wie folgt abfragen:

getconf ARG_MAX

das gibt die maximale Befehlszeilenlänge in Bytes aus. Ubuntu setzt dies beispielsweise standardmäßig auf, 2097152was ungefähr 2 MB bedeutet. Wenn ich mit unbegrenztem Stapel arbeite, erhalte ich 4611686018427387903genau 2^62 oder ungefähr 46000 TB. Wenn Ihre Befehlszeile überschreitetDas, ich gehe davon aus, dass Sie das Problem selbst umgehen können.

sudoBeachten Sie, dass das Problem nicht behoben werden kann, wenn Sie as in sudo mv *.dat somewhere/.running verwenden , ulimitda sudodie Stapelgröße vor der mvtatsächlichen Ausführung zurückgesetzt wird. Um das Problem zu umgehen, müssen Sie die Root-Shell mit starten sudo -s, dann ausführen ulimit -S -s unlimitedund schließlich den Befehl ohne in dieser Root-Shell ausführen sudo.

Antwort3

Manchmal ist es am einfachsten, einfach ein kleines Skript zu schreiben, zB in Python:

import glob, shutil

for i in glob.glob('*.jpg'):
  shutil.move(i, 'new_dir/' + i)

Antwort4

Die Argumentübergabebeschränkung des Betriebssystems gilt nicht für Erweiterungen, die innerhalb des Shell-Interpreters erfolgen. Daher können wir zusätzlich zu xargsoder findeinfach eine Shell-Schleife verwenden, um die Verarbeitung in einzelne Befehle aufzuteilen mv:

for x in *; do case "$x" in *.jpg|*.png|*.bmp) ;; *) mv -- "$x" target ;; esac ; done

Dabei werden nur die Funktionen und Dienstprogramme der POSIX Shell-Befehlssprache verwendet. Dieser Einzeiler ist durch Einrückungen und das Entfernen unnötiger Semikolons klarer:

for x in *; do
  case "$x" in
    *.jpg|*.png|*.bmp) 
       ;; # nothing
    *) # catch-all case
       mv -- "$x" target
       ;;
  esac
done

verwandte Informationen