Ich habe einen Ordner mit mehr als einer Million Dateien, der sortiert werden muss, aber ich kann nichts tun, weil mv
diese 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
xargs
ist das richtige Werkzeug für diese Aufgabe. Das oderfind
mit -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 mv
ist das Ziel. Bei GNU-Dienstprogrammen (also unter nicht eingebettetem Linux oder Cygwin) ist die -t
Option to mv
nü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 echo
Befehl 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 --
-0
Sie können die Option verwenden , um xargs
anstelle 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 diemv
eingebaut:
setopt extended_glob
zmodload zsh/files
mv -- ^*.(jpg|png|bmp) targetdir/
oder wenn Sie es vorziehen, mv
andere 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 mv
undzargs
:
autoload -U zargs
setopt extended_glob
zargs -- ./^*.(jpg|png|bmp) -- mv -t targetdir/ --
¹ Bei einigen xargs
Implementierungen 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, 2097152
was ungefähr 2 MB bedeutet. Wenn ich mit unbegrenztem Stapel arbeite, erhalte ich 4611686018427387903
genau 2^62 oder ungefähr 46000 TB. Wenn Ihre Befehlszeile überschreitetDas, ich gehe davon aus, dass Sie das Problem selbst umgehen können.
sudo
Beachten Sie, dass das Problem nicht behoben werden kann, wenn Sie as in sudo mv *.dat somewhere/.
running verwenden , ulimit
da sudo
die Stapelgröße vor der mv
tatsä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 unlimited
und 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 xargs
oder find
einfach 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