it-swarm.com.de

Argumentliste zu langer Fehler für Befehle rm, cp, mv

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?

474
Vicky

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
679
DPlusV

tl; dr

Dies ist eine Kernelbeschränkung der Größe des Befehlszeilenarguments. Verwenden Sie stattdessen eine for -Schleife.

Ursprung des Problems

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

Lösung: Verwenden von for Loop

Verwenden 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.

Lösung: Verwenden von 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 ).

Verweise

339
Édouard Lopez

find hat eine -delete-Aktion:

find . -maxdepth 1 -name '*.pdf' -delete
172
ThiefMaster

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

18

Oder du kannst versuchen:

find . -name '*.pdf' -exec rm -f {} \;
10
Jon Lin

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.

8
BigMike

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
7
Bibin Joseph

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.

5
danjperron

sie können dieses Lob verwenden 

find -name "*.pdf"  -delete
4
Sarath Ak

find . -type f -name '*xxx' -print -delete

3
pigletfly

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 {} \;
3
Fabio Farath

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

2
user3405020

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. 

1
lind

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, aber ulimit -s 65536 scheint mir oft zu helfen.

1
dps

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.

1
Pedro Montero

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 

0
Keithhn

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 {} \;
0
raja

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!

0
asatsi

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.

0
user219776

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)
0
mmann1123

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

0
Raman Kathpalia

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
0
Ecker00