Ich habe mehrere hundert PDFs unter einem Verzeichnis in 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öse ich diese Befehle?
Der Grund dafür ist, dass bash das Sternchen tatsächlich auf jede übereinstimmende Datei erweitert, wodurch eine sehr lange Befehlszeile erzeugt wird.
Versuche dies:
find . -name "*.pdf" -print0 | xargs -0 rm
Warnung: Dies ist eine rekursive Suche, die auch Dateien in Unterverzeichnissen findet (und löscht). Beziehen Sie -f
nur dann auf den Befehl rm, wenn Sie sicher sind, dass Sie keine Bestätigung wünschen.
Sie können folgendermaßen vorgehen, um den Befehl nicht rekursiv zu machen:
find . -maxdepth 1 -name "*.pdf" -print0 | xargs -0 rm
Eine weitere Option ist das -delete
-Flag von find:
find . -name "*.pdf" -delete
Dies ist eine Kernelbeschränkung der Größe des Befehlszeilenarguments. Verwenden Sie stattdessen eine for
-Schleife.
Dies ist ein Systemproblem, das mit der Konstante execve
und ARG_MAX
zusammenhängt. Es gibt eine Menge Dokumentation darüber (siehe man execve , debian's wiki ).
Grundsätzlich erzeugt die Erweiterung einen Befehl (mit seinen Parametern), der die ARG_MAX
Grenze überschreitet. Auf dem Kernel 2.6.23
wurde das Limit auf 128 kB
gesetzt. Diese Konstante wurde erhöht und Sie können ihren Wert erhalten, indem Sie Folgendes ausführen:
getconf ARG_MAX
# 2097152 # on 3.5.0-40-generic
for
LoopVerwenden Sie eine for
-Schleife, wie in BashFAQ/095 empfohlen, und es gibt keine Begrenzung außer für RAM/Speicherplatz:
for f in *.pdf; do rm "$f"; done
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 besser zu warten, da es komplexere Szenarien anpassen kann, zB wo man mehr als nur eine Aktion machen möchte.
find
Wenn Sie darauf bestehen, können Sie find
verwenden, aber wirklich verwenden Sie keine xargs , da es "gefährlich ist ( kaputt, ausnutzbar usw.) beim Lesen von nicht durch NUL getrennten Eingaben ":
find . -maxdepth 1 -name '*.pdf' -delete
Die Verwendung von -maxdepth 1 ... -delete
anstelle von -exec rm {} +
ermöglicht es find
, die erforderlichen Systemaufrufe einfach selbst auszuführen, ohne einen externen Prozess zu verwenden, und damit schneller (dank @ chepner comment ).
find
hat eine -delete
-Aktion:
find . -maxdepth 1 -name '*.pdf' -delete
Eine andere Antwort ist, xargs
zu zwingen, die Befehle in Batches zu verarbeiten. Zum Beispiel delete
die Dateien 100
auf einmal cd
in das Verzeichnis und führen Sie folgendes aus:
echo *.pdf | xargs -n 100 rm
Oder du kannst versuchen:
find . -name '*.pdf' -exec rm -f {} \;
sie können dies versuchen:
for f in *.pdf
do
rm $f
done
BEARBEITEN: ThiefMasters Kommentar schlägt vor, dass ich den jungen Shells jedis keine so gefährliche Praxis offenlegen sollte. Ich füge eine "sicherere" Version hinzu (um Dinge zu erhalten, wenn jemand ein "-rf. ..Pdf" hat Datei)
echo "# Whooooo" > /tmp/dummy.sh
for f in '*.pdf'
do
echo "rm -i $f" >> /tmp/dummy.sh
done
Öffnen Sie nach dem Ausführen des obigen Befehls die Datei /tmp/dummy.sh in Ihrem Favoritenordner. Editor und überprüfen Sie jede einzelne Zeile auf gefährliche Dateinamen und kommentieren Sie sie, wenn sie gefunden werden.
Kopieren Sie dann das dummy.sh-Skript in Ihr Arbeitsverzeichnis und führen Sie es aus.
Das alles aus Sicherheitsgründen.
Wenn Sie versuchen, eine sehr große Anzahl von Dateien gleichzeitig zu löschen (ich habe heute ein Verzeichnis mit 485.000+ gelöscht), wird dieser Fehler wahrscheinlich auftreten:
/bin/rm: Argument list too long.
Das Problem ist, dass, wenn Sie etwas wie rm -rf *
eingeben, der *
durch eine Liste aller übereinstimmenden Dateien ersetzt wird, z. B. „rm -rf file1 file2 file3 file4“ usw. Zum Speichern dieser Liste von Argumenten ist ein relativ kleiner Speicherpuffer zugeordnet. Wenn die Liste voll ist, führt die Shell das Programm nicht aus.
Um dieses Problem zu umgehen, verwenden viele Leute den find-Befehl, um jede Datei zu finden und sie einzeln an den Befehl "rm" zu übergeben:
find . -type f -exec rm -v {} \;
Mein Problem ist, dass ich 500.000 Dateien löschen musste und es dauerte viel zu lange.
Ich bin auf eine viel schnellere Methode zum Löschen von Dateien gestoßen - der Befehl "find" hat ein "-delete" -Flag eingebaut. Ich habe am Ende verwendet:
find . -type f -delete
Mit dieser Methode löschte ich Dateien mit einer Geschwindigkeit von etwa 2000 Dateien/Sekunde - viel schneller!
Sie können die Dateinamen auch anzeigen, wenn Sie sie löschen:
find . -type f -print -delete
… Oder sogar zeigen, wie viele Dateien gelöscht werden, und wie lange es dauert, sie zu löschen:
[email protected]# ls -1 | wc -l && time find . -type f -delete
100000
real 0m3.660s
user 0m0.036s
sys 0m0.552s
Sie könnten ein Bash-Array verwenden:
files=(*.pdf)
for((I=0;I<${#files[*]};I+=1000)); do rm -f ${files[@]:I:1000}; done
Auf diese Weise werden 1.000 Dateien pro Schritt gelöscht.
sie können dieses Lob verwenden
find -name "*.pdf" -delete
find . -type f -name '*xxx' -print -delete
Der Befehl rm hat eine Einschränkung der Dateien, die Sie gleichzeitig entfernen können.
Eine Möglichkeit, sie zu entfernen, indem Sie die Befehlsbasis rm in Ihren Dateimustern mehrfach verwenden, z.
rm -f A*.pdf
rm -f B*.pdf
rm -f C*.pdf
...
rm -f *.pdf
Sie können sie auch mit find command entfernen:
find . -name "*.pdf" -exec rm {} \;
Wenn es sich um Dateinamen mit Leerzeichen oder Sonderzeichen handelt, verwenden Sie:
find -maxdepth 1 -name '*.pdf' -exec rm "{}" \;
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.
ich hatte das gleiche Problem beim Kopieren des Formularquellverzeichnisses zum Ziel
quellverzeichnis hatte Dateien ~ 3 Lakcs
ich habe cp mit der Option -r verwendet und es hat für mich funktioniert
cp -r abc/def/
es werden alle Dateien von abc nach def kopiert, ohne dass die Argumentliste zu lange gewarnt wird
Und noch einer:
cd /path/to/pdf
printf "%s\0" *.[Pp][Dd][Ff] | xargs -0 rm
printf
ist eine eingebaute Shell, und soweit ich weiß, war sie schon immer so. Da printf
kein Shell-Befehl (sondern ein integrierter Befehl) ist, unterliegt er nicht dem schwerwiegenden "argument list too long ...
" -Fehler.
So können wir es sicher mit Shell-Globbing-Mustern wie *.[Pp][Dd][Ff]
verwenden, dann leiten wir seine Ausgabe an den Befehl remove (rm
) durch xargs
, der sicherstellt, dass genügend Dateinamen in die Befehlszeile passen, damit der Befehl rm
nicht fehlschlägt. Das ist ein Shell-Befehl.
Der \0
in printf
dient als Nulltrennzeichen für die Dateinamen, die dann vom xargs
-Befehl verarbeitet werden, wobei er (-0
) als Trennzeichen verwendet wird. rm
schlägt daher nicht fehl, wenn sich in den Dateinamen Leerzeichen oder andere Sonderzeichen befinden.
Ich bin ein paar Mal auf dieses Problem gestoßen. Viele Lösungen führen den Befehl rm
für jede einzelne Datei aus, die gelöscht werden muss. Das ist sehr ineffizient:
find . -name "*.pdf" -print0 | xargs -0 rm -rf
Am Ende habe ich ein Python-Skript geschrieben, um die Dateien basierend auf den ersten 4 Zeichen des Dateinamens zu löschen:
import os
filedir = '/tmp/' #The directory you wish to run rm on
filelist = (os.listdir(filedir)) #gets listing of all files in the specified dir
newlist = [] #Makes a blank list named newlist
for i in filelist:
if str((i)[:4]) not in newlist: #This makes sure that the elements are unique for newlist
newlist.append((i)[:4]) #This takes only the first 4 charcters of the folder/filename and appends it to newlist
for i in newlist:
if 'tmp' in i: #If statment to look for tmp in the filename/dirname
print ('Running command rm -rf '+str(filedir)+str(i)+'* : File Count: '+str(len(os.listdir(filedir)))) #Prints the command to be run and a total file count
os.system('rm -rf '+str(filedir)+str(i)+'*') #Actual Shell command
print ('DONE')
Das hat sehr gut für mich funktioniert. Ich konnte innerhalb von 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, der über wenig oder gar kein Python-Wissen verfügt, diesen Code manipulieren kann.
Sie können einen temporären Ordner erstellen, alle Dateien und Unterordner, die Sie behalten möchten, in den temporären Ordner verschieben, den alten Ordner löschen und den temporären Ordner in den alten Ordner umbenennen.
mkdir testit
cd testit
mkdir big_folder tmp_folder
touch big_folder/file1.pdf
touch big_folder/file2.pdf
mv big_folder/file1,pdf tmp_folder/
rm -r big_folder
mv tmp_folder big_folder
der rm -r big_folder
entfernt alle Dateien im big_folder
, egal wieviele. Sie müssen nur sehr vorsichtig sein, dass Sie zuerst alle Dateien/Ordner haben, die Sie behalten möchten. In diesem Fall war es file1.pdf
Versuchen Sie dies auch Wenn Sie Dateien/Ordner mit mehr als 30/90 Tagen (+) oder auch mit weniger als 30/90 (-) Tagen löschen möchten, können Sie die folgenden ex-Befehle verwenden
Bsp .: Für 90 Tage ausgenommen nach 90 Tagen Löschung von Dateien/Ordnern, bedeutet dies 91,92 ... 100 Tage
find <path> -type f -mtime +90 -exec rm -rf {} \;
Beispiel: Verwenden Sie für die neuesten 30-Tage-Dateien, die Sie löschen möchten, den folgenden Befehl (-).
find <path> -type f -mtime -30 -exec rm -rf {} \;
Wenn Sie die Dateien für mehr als 2 Tage archivieren möchten
find <path> -type f -mtime +2 -exec gzip {} \;
Wenn Sie die Dateien/Ordner nur aus dem letzten Monat sehen möchten .. Bsp .:
find <path> -type f -mtime -30 -exec ls -lrt {} \;
Bei mehr als 30 Tagen mehr nur die Dateien/Ordner
find <path> -type f -mtime +30 -exec ls -lrt {} \;
find /opt/app/logs -type f -mtime +30 -exec ls -lrt {} \;
Ich war mit einem ähnlichen Problem konfrontiert, als Millionen von nutzlosen Protokolldateien von einer Anwendung erstellt wurden, die alle Inodes ausfüllte. Ich habe auf "locate" zurückgegriffen, alle Dateien in einer Textdatei "gefunden" und dann nacheinander entfernt. Es dauerte eine Weile, aber erledigte den Job!
Ich kenne nur einen Weg, um dies zu umgehen. Die Idee ist, die Liste der PDF-Dateien, die Sie haben, in eine Datei zu exportieren. Dann teilen Sie diese Datei in mehrere Teile. Entfernen Sie dann die in jedem Teil aufgeführten PDF-Dateien.
ls | grep .pdf > list.txt
wc -l list.txt
wc -l zählt, wie viele Zeilen die list.txt enthält. Wenn Sie die Vorstellung haben, wie lange es dauert, können Sie sich entscheiden, es in zwei Hälften zu teilen. Verwenden Sie den Befehl split -l Teilen Sie ihn beispielsweise in jeweils 600 Zeilen.
split -l 600 list.txt
dadurch werden einige Dateien mit den Namen xaa, xab, xac usw. erstellt. Dies hängt davon ab, wie Sie die Datei teilen.
rm $(<xaa)
rm $(<xab)
rm $(<xac)
Entschuldigung für mein schlechtes Englisch.
Ich fand, dass für extrem große Listen von Dateien (> 1e6) diese Antworten zu langsam waren. Hier ist eine Lösung, die Parallelverarbeitung in Python verwendet. Ich weiß, ich weiß, das ist kein Linux ... aber sonst hat nichts funktioniert.
(Das hat mir Stunden gespart)
# delete files
import os as os
import glob
import multiprocessing as mp
directory = r'your/directory'
os.chdir(directory)
files_names = [i for i in glob.glob('*.{}'.format('pdf'))]
# report errors from pool
def callback_error(result):
print('error', result)
# delete file using system command
def delete_files(file_name):
os.system('rm -rf ' + file_name)
pool = mp.Pool(12)
# or use pool = mp.Pool(mp.cpu_count())
if __== '__main__':
for file_name in files_names:
print(file_name)
pool.apply_async(delete_files,[file_name], error_callback=callback_error)
Alle *.pdf
in einem Verzeichnis löschen /path/to/dir_with_pdf_files/
mkdir empty_dir # Create temp empty dir
rsync -avh --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/
Das Löschen bestimmter Dateien über rsync
mit Platzhalterzeichen ist wahrscheinlich die schnellste Lösung für den Fall, dass Sie Millionen von Dateien haben. Und es wird sich um den Fehler kümmern, den Sie bekommen.
(Optionaler Schritt): DRY RUN. Um zu prüfen, was gelöscht wird, ohne zu löschen. `
rsync -avhn --delete --include '*.pdf' empty_dir/ /path/to/dir_with_pdf_files/
...
Klicken Sie auf rsync Tipps und Tricks für weitere Rsync-Hacks
Wenn Sie beim Löschen einer großen Anzahl von Dateien einen Server oder ein System responsive beibehalten müssen, kann sleep
zwischen jeder Löschanweisung ein guter Ansatz sein.
find . -name "*.pdf" -print0 | while read -d $'\0' file
do
rm "$file"
sleep 0.005 # Sleeps for 5ms, Tweak as needed
done