Ich habe mehrere hundert PDFs in einem Verzeichnis unter UNIX. Die Namen der PDFs sind sehr lang (ca. 60 Zeichen).
Wenn ich versuche, alle PDFs zusammen mit dem folgenden Befehl zu löschen:
rm -f *.pdf
Ich erhalte folgende Fehlermeldung:
/bin/rm: cannot execute [Argument list too long]
Was ist die Lösung für diesen Fehler? Tritt dieser Fehler auch für mv
und cp
Befehle auf? Wenn ja, wie lösen Sie diese Befehle?
Antworten:
Der Grund dafür ist, dass bash das Sternchen tatsächlich auf jede übereinstimmende Datei erweitert und eine sehr lange Befehlszeile erzeugt.
Versuche dies:
Warnung: Dies ist eine rekursive Suche, bei der auch Dateien in Unterverzeichnissen gefunden (und gelöscht) werden. Setzen Sie
-f
den Befehl rm nur ein, wenn Sie sicher sind, dass Sie keine Bestätigung wünschen.Sie können Folgendes tun, um den Befehl nicht rekursiv zu machen:
Eine andere Option ist die Verwendung der
-delete
Flagge von find :quelle
xargs
teilt die Liste speziell auf und gibt bei Bedarf mehrere Befehle aus.-maxdepth 1
muss das erste Argument nach dem Pfad sein.-delete
Flag zum Löschen der gefundenen Dateien, und selbst wenn dies nicht der Fall wäre, wäre es immer noch besser-exec
, rm auszuführen, anstatt xargs aufzurufen (dies sind jetzt 3 Prozesse und eine Pipe anstelle eines einzelnen Prozesses mit-delete
oder 2 Prozesse mit-exec
).dangerous (broken, exploitable, etc.)
ist ziemlich lächerlich. Zweifellos sollten Sie bei der Verwendung vorsichtig seinxargs
, aber es ist nicht ganzeval/evil
.-exec
Aufrufenrm
beträgt die Anzahl der Prozesse 1 + Anzahl der Dateien, obwohl die Anzahl der gleichzeitigen Prozesse daraus 2 betragen kann (möglicherweise würde find gleichzeitig rm-Prozesse ausführen). Die Anzahl der verwendeten Prozessexargs
würde drastisch auf 2 + n reduziert, wobei n eine Anzahl von Prozessen ist, die kleiner als die Anzahl der Dateien sind (z. B. Anzahl der Dateien / 10, obwohl dies wahrscheinlich mehr von der Länge der Pfade abhängt). Angenommen, find führt das Löschen direkt durch,-delete
sollte using der einzige Prozess sein, der aufgerufen wird.tl; dr
Dies ist eine Kernelbeschränkung für die Größe des Befehlszeilenarguments. Verwenden Sie
for
stattdessen eine Schleife.Ursprung des Problems
Dies ist ein Systemproblem, das mit
execve
undARG_MAX
konstant ist. Es gibt eine Menge Dokumentation darüber (siehe man execve , Debians Wiki ).Grundsätzlich erzeugt die Erweiterung einen Befehl (mit seinen Parametern), der den
ARG_MAX
Grenzwert überschreitet. Auf dem Kernel2.6.23
wurde das Limit auf gesetzt128 kB
. Diese Konstante wurde erhöht und Sie können ihren Wert erhalten, indem Sie Folgendes ausführen:Lösung: Verwenden von
for
LoopVerwenden Sie eine
for
Schleife, wie sie in BashFAQ / 095 empfohlen wird, und es gibt keine Begrenzung außer für RAM / Speicherplatz:Durch einen Trockenlauf wird sichergestellt, dass das, was Sie erwarten, gelöscht wird:
Und führen Sie es aus:
Dies ist auch ein portabler Ansatz, da Glob ein starkes und konsistentes Verhalten zwischen Shells aufweist ( Teil der POSIX-Spezifikation ).
Hinweis: Wie in mehreren Kommentaren erwähnt, ist dies zwar langsamer, aber wartbarer, da komplexere Szenarien angepasst werden können, z. B. wenn mehr als nur eine Aktion ausgeführt werden soll.
Lösung: Verwenden
find
Wenn Sie darauf bestehen, können Sie xargs verwenden,
find
aber wirklich nicht, da es "gefährlich (kaputt, ausnutzbar usw.) beim Lesen von nicht durch NUL getrennten Eingaben ist" :Die Verwendung von
-maxdepth 1 ... -delete
anstelle von-exec rm {} +
ermöglichtfind
es, die erforderlichen Systemaufrufe einfach selbst auszuführen, ohne einen externen Prozess zu verwenden, und ist daher schneller (dank des Kommentars von @chepner ).Verweise
quelle
for
Schleife. Ich habe es schon einmal benutztfind
, aber ich schaue immer nach, wie es geht, da ich die Optionen usw. die ganze Zeit vergesse.for
scheint leichter zu erinnern IMHOfor f in *; do rm "$f"; done
Arbeit als Zauberfind -exec
Lösung scheint VIEL schneller zu sein als diefor
Schleife.4.15.0-1019-gcp
um genau zu sein) und das Limit liegt immer noch bei 2097152. Interessanterweise ergibt die Suche nach ARG_MAX im Linux Git Repo ein Ergebnis, das zeigt, dass ARG_MAX bei 131702 liegt.find
hat eine-delete
Aktion:quelle
xargs
gemäß Dennis 'Antwort funktioniert wie beabsichtigt.-exec
besteht, eine Reihe von Dateien zu entfernen.-exec rm {} +
würde das gleiche tun, erfordert aber immer noch den Start mindestens eines externen Prozesses.-delete
ermöglichtfind
die einfache Ausführung der erforderlichen Systemaufrufe selbst ohne Verwendung eines externen Wrappers.Eine andere Antwort besteht darin,
xargs
die Verarbeitung der Befehle in Stapeln zu erzwingen . Zum Beispiel zudelete
den Dateien100
gleichzeitigcd
in das Verzeichnis und führen Sie Folgendes aus:echo *.pdf | xargs -n 100 rm
quelle
echo
eine Shell eingebaut ist. Wenn Sie den Befehl verwendenecho
, stoßen Sie immer noch auf das Programmargumentlimit.Oder Sie können versuchen:
quelle
find . -maxdepth 1 -name '*.pdf' -exec rm -f {} \;
Wenn Sie versuchen, eine sehr große Anzahl von Dateien gleichzeitig zu löschen (ich habe heute ein Verzeichnis mit mehr als 485.000 gelöscht), wird wahrscheinlich dieser Fehler auftreten:
Das Problem ist, dass beim Eingeben von etwas wie
rm -rf *
das*
durch eine Liste aller übereinstimmenden Dateien ersetzt wird, z. B. "rm -rf Datei1 Datei2 Datei3 Datei4" und so weiter. Es gibt einen relativ kleinen Speicherpuffer zum Speichern dieser Liste von Argumenten, und wenn er voll ist, führt die Shell das Programm nicht aus.Um dieses Problem zu umgehen, verwenden viele Benutzer den Befehl find, um jede Datei zu finden und sie einzeln wie folgt an den Befehl „rm“ zu übergeben:
Mein Problem ist, dass ich 500.000 Dateien löschen musste und es viel zu lange dauerte.
Ich bin auf eine viel schnellere Methode zum Löschen von Dateien gestoßen - der Befehl "find" hat ein eingebautes "-delete" -Flag! Folgendes habe ich letztendlich verwendet:
Mit dieser Methode löschte ich Dateien mit einer Geschwindigkeit von ungefähr 2000 Dateien / Sekunde - viel schneller!
Sie können die Dateinamen auch anzeigen, wenn Sie sie löschen:
… Oder zeigen Sie sogar, wie viele Dateien gelöscht werden und wie lange es dauert, sie zu löschen:
quelle
sudo find . -type f -delete
ungefähr 485.000 Dateien gelöscht und es hat bei mir funktioniert. Dauerte ungefähr 20 Sekunden.Sie können dies versuchen:
BEARBEITEN: ThiefMaster-Kommentar schlägt mir vor, solche gefährlichen Praktiken nicht an die Jedis junger Muscheln weiterzugeben, daher werde ich eine "sicherere" Version hinzufügen (um die Dinge zu bewahren, wenn jemand eine "-rf. ..Pdf" -Datei hat).
Nachdem Sie die oben genannten Schritte ausgeführt haben, öffnen Sie einfach die Datei /tmp/dummy.sh in Ihrem Favoriten. Bearbeiten Sie jede einzelne Zeile auf gefährliche Dateinamen und kommentieren Sie sie aus, wenn sie gefunden werden.
Kopieren Sie dann das Skript dummy.sh in Ihr Arbeitsverzeichnis und führen Sie es aus.
All dies aus Sicherheitsgründen.
quelle
-rf .. .pdf
-rf
hat Vorrang-i
, daher ist Ihre 2. Version nicht besser (ohne manuelle Überprüfung). Und ist im Grunde genommen für das Massenlöschen nutzlos, da für jede Datei eine Aufforderung erfolgt.Sie könnten ein Bash-Array verwenden:
Auf diese Weise werden Stapel von 1000 Dateien pro Schritt gelöscht.
quelle
Sie können dieses Lob verwenden
quelle
Der Befehl rm enthält eine Einschränkung von Dateien, die Sie gleichzeitig entfernen können.
Eine Möglichkeit, sie zu entfernen, indem Sie den Befehl rm mehrmals auf der Grundlage Ihrer Dateimuster verwenden, z.
Sie können sie auch über den Befehl find entfernen :
quelle
rm
die Anzahl der zu verarbeitenden Dateien ist nicht begrenzt (außer dassargc
sie nicht größer sein dürfen alsINT_MAX
). Dies ist die Beschränkung des Kernels auf die maximale Größe des gesamten Argumentarrays (aus diesem Grund ist die Länge der Dateinamen erheblich).Wenn es sich um Dateinamen mit Leerzeichen oder Sonderzeichen handelt, verwenden Sie:
Dieser Satz durchsucht alle Dateien im aktuellen Verzeichnis (-maxdepth 1) mit der Erweiterung pdf (-name '* .pdf') und löscht dann jede (-exec rm "{}").
Der Ausdruck {} ersetzt den Namen der Datei und "{}" legt den Dateinamen als Zeichenfolge fest, einschließlich Leerzeichen oder Sonderzeichen.
quelle
-exec
ist, dass Sie keine Shell aufrufen. Die Zitate hier machen absolut nichts Nützliches. (Sie verhindern eine Platzhaltererweiterung und Token-Aufteilung für die Zeichenfolge in der Shell, in die Sie diesen Befehl{}
Ich hatte das gleiche Problem beim Kopieren des Formularquellverzeichnisses zum Ziel
Quellverzeichnis hatte Dateien ~ 3 Lakcs
Ich habe CP mit Option -r verwendet und es hat bei mir funktioniert
cp -r abc / def /
Es kopiert alle Dateien von abc nach def, ohne zu lange vor der Argumentliste zu warnen
quelle
Versuchen Sie dies auch Wenn Sie Dateien / Ordner über 30/90 Tage (+) oder unter 30/90 (-) Tagen löschen möchten, können Sie die folgenden ex-Befehle verwenden
Beispiel: Für 90-Tage-Ausschlüsse nach 90-tägigem Löschen von Dateien / Ordnern bedeutet dies 91,92 .... 100 Tage
Beispiel: Verwenden Sie für nur die letzten 30-Tage-Dateien, die Sie löschen möchten, den folgenden Befehl (-).
Wenn Sie die Dateien für mehr als 2 Tage Dateien giz möchten
Wenn Sie die Dateien / Ordner nur aus dem letzten Monat sehen möchten. Ex:
Über 30 Tage mehr nur dann die Dateien / Ordner auflisten Beispiel:
quelle
Ich bin überrascht, dass es hier keine
ulimit
Antworten gibt. Jedes Mal, wenn ich dieses Problem habe, lande ich hier oder hier . Ich verstehe, dass diese Lösung Einschränkungen hat, aberulimit -s 65536
oft den Trick für mich zu tun scheint.quelle
Ich hatte das gleiche Problem mit einem Ordner voller temporärer Bilder, der von Tag zu Tag größer wurde, und dieser Befehl half mir, den Ordner zu löschen
Der Unterschied zu den anderen Befehlen besteht im Parameter mtime, der nur die Dateien akzeptiert, die älter als X Tage sind (im Beispiel 50 Tage).
Durch mehrmaliges Verwenden konnte ich bei jeder Ausführung im Tagesbereich alle unnötigen Dateien entfernen
quelle
Ich kenne nur einen Weg, um das zu umgehen. Die Idee ist, diese Liste der PDF-Dateien, die Sie haben, in eine Datei zu exportieren. Teilen Sie diese Datei dann in mehrere Teile auf. Entfernen Sie dann die in den einzelnen Teilen aufgeführten PDF-Dateien.
wc -l zählt, wie viele Zeilen die list.txt enthält. Wenn Sie die Idee haben, wie lange es dauert, können Sie entscheiden, es in zwei Hälften, vier oder so zu teilen. Verwenden des Befehls split -l Teilen Sie ihn beispielsweise in jeweils 600 Zeilen auf.
Dadurch werden einige Dateien mit den Namen xaa, xab, xac usw. erstellt. Dies hängt davon ab, wie Sie sie aufteilen. Verwenden Sie nun Folgendes, um jede Liste in diesen Dateien in den Befehl rm zu "importieren":
Entschuldigung für mein schlechtes Englisch.
quelle
pdf_format_sucks.docx
haben, wird diese ebenfalls gelöscht ... ;-) Sie sollten beim Suchen nach PDF-Dateien den richtigen und genauen regulären Ausdruck verwenden.still_pdf_format_sucks.docx
wird aber gelöscht. Der Punkt.
im".pdf"
regulären Ausdruck entspricht einem beliebigen Zeichen. Ich würde"[.]pdf$"
stattdessen vorschlagen.pdf
.Ich bin ein paar Mal auf dieses Problem gestoßen. Viele der Lösungen führen den
rm
Befehl für jede einzelne Datei aus, die gelöscht werden muss. Das ist sehr ineffizient:Am Ende habe ich ein Python-Skript geschrieben, um die Dateien basierend auf den ersten 4 Zeichen im Dateinamen zu löschen:
Das hat bei mir sehr gut funktioniert. Ich konnte in etwa 15 Minuten über 2 Millionen temporäre Dateien in einem Ordner löschen. Ich habe den Teer aus dem kleinen Code heraus kommentiert, damit jeder mit minimalen bis keinen Python-Kenntnissen diesen Code manipulieren kann.
quelle
Und noch einer:
printf
ist eine Shell eingebaut, und soweit ich weiß, war es immer so. Da diesprintf
kein Shell-Befehl (sondern ein eingebauter) ist, kann es nicht zu einemargument list too long ...
schwerwiegenden Fehler kommen.Wir können es also sicher mit Shell-Globbing-Mustern verwenden, z. B.
*.[Pp][Dd][Ff]
dann leiten wir seine Ausgabe an denrm
Befehl remove ( ) weiter, umxargs
sicherzustellen, dass genügend Dateinamen in die Befehlszeile passen, damit derrm
Befehl, bei dem es sich um eine Shell handelt, nicht fehlschlägt Befehl.Das
\0
inprintf
dient als Nulltrennzeichen für die Dateinamen, die dann perxargs
Befehl verarbeitet werden , wobei it (-0
) als Trennzeichen verwendet wird. Esrm
schlägt also nicht fehl, wenn die Dateinamen Leerzeichen oder andere Sonderzeichen enthalten.quelle
printf
keine Shell eingebaut ist, unterliegt sie derselben Einschränkung.Sie können einen temporären Ordner erstellen, alle Dateien und Unterordner, die Sie behalten möchten, in den temporären Ordner verschieben, dann den alten Ordner löschen und den temporären Ordner in den alten Ordner umbenennen. Versuchen Sie dieses Beispiel, bis Sie sicher sind, dass Sie es live ausführen können:
Das
rm -r big_folder
wird alle Dateien entfernen,big_folder
egal wie viele. Sie müssen nur sehr vorsichtig sein, wenn Sie zuerst alle Dateien / Ordner haben, die Sie behalten möchten, in diesem Fall war es dasfile1.pdf
quelle
Alle
*.pdf
in einem Verzeichnis löschen/path/to/dir_with_pdf_files/
Das Löschen bestimmter Dateien
rsync
mithilfe eines Platzhalters ist wahrscheinlich die schnellste Lösung, wenn Sie Millionen von Dateien haben. Und es wird sich um Fehler kümmern, die Sie bekommen.(Optionaler Schritt): DRY RUN. Um zu überprüfen, was gelöscht wird, ohne zu löschen. `
. . .
Klicken Sie auf rsync-Tipps und -Tricks, um weitere rsync-Hacks zu erhalten
quelle
Ich fand, dass diese Antworten für extrem große Listen von Dateien (> 1e6) zu langsam waren. Hier ist eine Lösung mit paralleler Verarbeitung in Python. Ich weiß, ich weiß, das ist kein Linux ... aber sonst hat hier nichts funktioniert.
(Das hat mir Stunden gespart)
quelle
Ich hatte ein ähnliches Problem, als Millionen nutzloser Protokolldateien von einer Anwendung erstellt wurden, die alle Inodes füllte. Ich habe auf "Suchen" zurückgegriffen, alle "gefundenen" Dateien in eine Textdatei aufgenommen und sie dann einzeln entfernt. Hat eine Weile gedauert, aber den Job gemacht!
quelle
locate
zurück installiert haben, als Sie noch Platz auf Ihrer Festplatte hatten.Eine etwas sicherere Version als die Verwendung von xargs, auch nicht rekursiv:
ls -p | grep -v '/$' | grep '\.pdf$' | while read file; do rm "$file"; done
Das Filtern unserer Verzeichnisse hier ist etwas unnötig, da 'rm' es sowieso nicht löscht und es der Einfachheit halber entfernt werden kann. Aber warum etwas ausführen, das definitiv einen Fehler zurückgibt?
quelle
ls
ist ein häufiges Antimuster, das unbedingt vermieden werden sollte, und fügt hier eine Reihe zusätzlicher Fehler hinzu. Dasgrep | grep
ist einfach nicht sehr elegant.find
sind gut und hier und anderswo gut dokumentiert. Weitere Informationen zu diesem und verwandten Themen finden Sie beispielsweise auf mywiki.wooledge.org .Die Verwendung von GNU parallel (
sudo apt install parallel
) ist sehr einfachEs führt die Befehle multithreaded aus, wobei '{}' das übergebene Argument ist
Z.B
ls /tmp/myfiles* | parallel 'rm {}'
quelle
ls
direkte Übergabe der Ausgabe an andere Befehle ein gefährliches Gegenmuster ist - das und die Tatsache, dass die Erweiterung des Platzhalters bei der Ausführung denselben Fehler verursachtls
wie beim ursprünglichenrm
Befehl .parallel
einige Leute, die es vorziehen, Komplexität zu vermeiden, fühlen sich unwohl - wenn Sie unter die Haube schauen, ist es ziemlich undurchsichtig. Weitere Informationen finden Sie im Mailinglisten-Thread unter lists.gnu.org/archive/html/bug-parallel/2015-05/msg00005.html zwischen Stephane (einem der Unix- und Linux- StackExchange-Graubärte) und Ole Tange (Parallel-Autor).xargs -P
paralellisiert auch, aber es macht es auf eine einfachere, dümmere Weise mit weniger beweglichen Teilen, wodurch sein Verhalten viel einfacher vorherzusagen und zu überlegen ist.Zum Entfernen der ersten 100 Dateien:
rm -rf 'ls | Kopf -100 '
quelle
Die folgende Option scheint für dieses Problem einfach zu sein. Ich habe diese Informationen von einem anderen Thread erhalten, aber es hat mir geholfen.
Führen Sie einfach den obigen Befehl aus und es wird die Aufgabe erledigen.
quelle