Löschen Sie alle Dateien in einem Verzeichnis, deren Name nicht mit einer Zeile in einer Dateiliste übereinstimmt

9

Ich habe ein Verzeichnis mit mehr als 1000 Dateien. In einer Textdatei habe ich ungefähr 50 Dateinamen, einen pro Zeile. Ich möchte alle Dateien im Verzeichnis löschen, deren Dateinamen keinem Eintrag in der Liste entsprechen. Was ist der beste Weg, dies zu tun? Ich habe ein Shell-Skript gestartet, konnte jedoch nicht den richtigen Befehl ermitteln, um festzustellen, ob der Dateiname auf der Liste steht. Vielen Dank.

Nathan
quelle

Antworten:

8

Mir ist klar, dass jede Frage zum Löschen von Dateien mit großer Sorgfalt beantwortet werden muss. Meine erste Antwort war zu voreilig. Ich habe nicht berücksichtigt, dass die Dateiliste fehlerhaft sein kann, um mit egrep verwendet zu werden. Ich habe die Antwort bearbeitet, um dieses Risiko zu verringern.

Das sollte für die Dateien funktionieren, die keinen Platz im Namen haben:

Erstellen Sie zuerst Ihre Dateiliste neu, um sicherzustellen, dass sie mit dem genauen Dateinamen übereinstimmt:

sed -e 's,^,^,' -e 's,$,$,'  filelist  > newfilelist 

Erstellen Sie die rm-Befehle

cd your_directory
ls | egrep -vf newfilelist   | xargs -n 1 echo rm  >  rmscript

Überprüfen Sie, ob das rm-Skript zu Ihnen passt (Sie können es mit "vim" oder "less" tun).
Führen Sie dann die folgende Aktion aus:

sh -x rmscript

Wenn die Dateien Leerzeichen in ihrem Namen haben (wenn die Dateien das Leerzeichen im Namen haben, "funktioniert dies nicht):

ls | egrep -vf newfilelist  | sed 's,^\(.*\)$,rm "\1",' > rmscript

Natürlich sollte sich die Dateiliste nicht im selben Verzeichnis befinden!

EDITIERT:

Die Dateiliste des Nathan enthielt Namen, die mit allen Dateien im Verzeichnis übereinstimmten (wie "html" entspricht "bob.html"). Es wurde also nichts gelöscht, weil egrep -vfder gesamte Stream absorbiert wurde. Ich habe einen Befehl hinzugefügt, um jedem Dateinamen ein "^" und ein "$" hinzuzufügen. Ich hatte Glück, dass Nathans Dateiliste korrekt war. Wäre es DOS-formatiert mit CR-LF-Endzeilen oder mit zusätzlichen Leerzeichen gewesen, wären vom egrep keine Dateien erhalten und alle gelöscht worden.

Emmanuel
quelle
Wenn ich den Vorschau-Befehl ausführe, erhalte ich eine Zeile mit "rm". Wenn ich den eigentlichen Befehl ausführe, wird eine Fehlermeldung über fehlende Argumente für rm angezeigt. Benötige ich eine spezielle Syntax, um die Ergebnisse von ls | zu verwenden? egrep in der xargs Eingabe?
Nathan
@ Nathan müssen Sie zuerst in Ihr Verzeichnis cd. Kein spezieller Syntaxt. lsliefert die Verzeichnisdateinamen, egrep -vf filelistfiltert Ihre 50 Dateinamen. Ich fürchte, Sie haben alle Ihre Dateien gelöscht.
Emmanuel
@Emamanuel Ich führe den Befehl aus dem Verzeichnis aus, das zu löschende Dateien enthält.
Nathan
@ Nathan sind alle deine Dateien gelöscht?
Emmanuel
Nein, sie sind immer noch da.
Nathan
1

Konstruieren Sie die Argumente vor find:

{
  read -r
  keep=( -name "$REPLY" ) # no `-o` before the first one.
  while read -r; do
    keep+=( -o -name "$REPLY" )
  done
} < file_list.txt
find . -type f ! \( "${keep[@]}" \) -exec echo rm {} +

Verwenden Sie die echoTeile, um zu sehen, was konstruiert werden würde. Entfernen Sie die echoTeile, um es tatsächlich auszuführen.

Update: Demonstration:

##
# Demonstrate what files exist for testing.
# Show their whitespace:
~/foo $ printf '"%s"\n' *
" op"
" qr"
"abc"
"def"
"gh "
"ij "
"k l"
"keep"
"m n"

##
# Show the contents of the "keep" file,
# Including its whitespace:
~/foo $ cat -e keep
keep$
abc$
gh $
k l$
 op$

##
# Execute the script:
~/foo $ { read -r; keep=( -name "$REPLY" ); while read -r ; do keep+=( -o -name "$REPLY" ); done } < keep
~/foo $ find . -type f ! \( "${keep[@]}" \) -exec rm {} +

##
# Show what files remain:
~/foo $ printf '"%s"\n' *
" op"
"abc"
"gh "
"k l"
"keep"
Kojiro
quelle
Ich mag dieses am besten, da es die Notwendigkeit einer Dateiliste beseitigt
eyoung100
+1 von mir, obwohl es nicht sehr gut mit Leerzeichen umgeht. Vielleicht sollten einige einfache Anführungszeichen ( ') hinzugefügt werden, dh keep=( -name \'"$REPLY"\' )und keep+=( -o -name \'"$REPLY"\' ).
Cristian Ciupitu
Das oben Genannte ist gefährlich, da Sie versehentlich Dateien löschen können.
Davidva
@CristianCiupitu nicht wahr? Ich habe eine Demo hinzugefügt, die zeigt, dass es sehr gut mit Leerzeichen umgeht.
Kojiro
@davidva Unter welchen Umständen? Jedes Mal, wenn Sie das Löschen von Dingen automatisieren, laufen Sie Gefahr, einen Fehler zu machen, aber innerhalb der Parameter der Frage denke ich, dass meine Demo beweist, dass dieser Ansatz richtig ist.
Kojiro
1

Mit zsh:

mylist=(${(f)"$(<filelist)"})
print -rl -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Es liest die Zeilen filelisteines Arrays und verwendet dann glob qualifiers / estring , um nur die Dateinamen zu globalisieren / auszuwählen, die nicht im Array vorhanden sind: Es werden .nur reguläre Dateien ausgewählt (hinzufügen, Dwenn Ihre Liste Punktdateien enthält) und das Negierte ^e_'expression'_wählt nur diejenigen für aus wobei der Ausdruck false zurückgibt, dh wenn ihr Name ( $REPLY) kein Element des Arrays ist .
Wenn Sie mit dem Ergebnis zufrieden sind, ersetzen Sie es print -rldurch rm, um die Dateien tatsächlich zu entfernen:

rm -- *(.^e_'(($mylist[(Ie)$REPLY]))'_)

Verwenden Sie den */**Glob mit ${REPLY:t}Glob-Modifikator, um Dateien rekursiv auszuwählen und zu entfernen :

rm -- */**(.^e_'(($mylist[(Ie)${REPLY:t}]))'_)
don_crissti
quelle
0

Wenn Sie den Inhalt des Verzeichnisses wie folgt in eine Datei einfügen:

cd <somedirectory>
ls >> filelist

Offene Dateiliste mit einem Texteditor und entfernen Sie alle Dateien , außer denen , die Sie löschen möchten . Das ist fett gedruckt, weil es der entgegengesetzte Ansatz zur obigen Antwort ist

Versuche dies:

while read p || [[ -n $p ]]; 
echo $p
done < filelist

Wenn Ihre Liste der auf dem Bildschirm ausgegebenen Dateien angezeigt wird, ersetzen Sie das Echo durch rm -v:

while read p || [[ -n $p ]]; 
rm -v $p
done < filelist
eyoung100
quelle
0

Führen Sie das folgende Skript aus.

  1. Zunächst finde ich alle Dateien, die im Verzeichnis vorhanden sind, und speichere die Ausgabe in einer anderen Datei all_files.
  2. Wir haben eine Datei mit der Liste der Dateien, die NICHT gelöscht werden sollten ( not_to_be_deleted_files).
  3. Ich füge die Dateinamen hinzu not_to_be_deleted_filesund files_to_be_deletedam Ende, not_to_be_deleted_fileswie wir diese 2 Dateien brauchen.
  4. Jetzt finde ich die Dateien, die mit dem Linux- joinBefehl gelöscht werden müssen, und leite die Ausgabe in eine files_to_be_deleted Datei um.
  5. Jetzt lese ich in der letzten while-Schleife alle Dateinamen ein files_to_be_deletedund entferne die in diesem Dateinamen genannten Dateien.

Das Skript ist wie folgt.

find /home/username/directory -type f | sed 's/.*\///' > all_files
echo all_files >> not_to_be_deleted_files
echo not_to_be_deleted_files >> not_to_be_deleted_files
echo files_to_be_deleted >> not_to_be_deleted_files
join -v 1 <(sort all_files_listed) <(sort files_not_to_be_deleted) >   files_to_be_deleted
while read file
rm  "$file"
done < files_to_be_deleted

PS : Wenn Sie möchten, dass dies als Skript gespeichert und ausgeführt wird, können Sie den Skriptnamen wahrscheinlich auch mit hinzufügen echo scriptname >> not_to_be_deleted_files.

Obwohl es nicht erforderlich ist, bevorzuge ich es, weil es später kein Bedauern geben wird. Ich habe auf einen kleinen Satz von Dateien getestet und es hat in meinem System funktioniert. Wenn Sie jedoch sicher sein möchten, versuchen Sie es zuerst in einem testVerzeichnis und entfernen Sie dann die Dateien im ursprünglichen Verzeichnis.

Ramesh
quelle
0
  • Verwenden Sie die Liste als Quelle, um alle Dateien in der Liste in ein neues und leeres Speicherverzeichnis zu verschieben.
  • Vergleichen Sie die Anzahl der Dateien in der Liste und die Anzahl der gespeicherten Dateien.
  • Wenn beide übereinstimmen, löschen Sie alle nicht gespeicherten Dateien mit Ihrer bevorzugten Methode.
  • Verschieben Sie die gespeicherten Dateien zurück.
Benutzer unbekannt
quelle
0

Ich habe mich für einen sichereren und viel, viel schnelleren Ansatz entschieden, weil ich 18.000 Dateien in der Liste hatte! Ich musste Bilder in einer großen Drupal-Installation bereinigen.

Das Löschen aller Dateien, die nicht in der Liste enthalten sind, entspricht dem Beibehalten nur der Dateien, die in der Liste enthalten sind. Daher habe ich beschlossen, die Dateien tatsächlich von der Liste an einen anderen Speicherort zu kopieren, aber das Kopieren von 20 GB Dateien würde zu viel Speicherplatz beanspruchen und auch sehr langsam sein. Der Trick besteht also darin, die Dateien hardlinksstattdessen mit der -lOption zu kopieren cp. Dies nimmt fast keinen Platz ein und ist sehr schnell. Da ich die Verzeichnisstruktur beibehalten musste, habe ich außerdem die --parentsOption verwendet.

Hier ist ein Auszug aus meiner Dateiliste:

1px.png
misc/feed.png
modules/file/icons/x-office-presentation.png
modules/file/icons/x-office-spreadsheet.png
newsletter.png
sites/all/libraries/ckeditor/plugins/smiley/images/devil_smile.png
sites/all/libraries/ckeditor/plugins/smiley/images/regular_smile.png
sites/default/files/009313_PwC_banner_CBS_Observer_180x246px.jpg

Eine Beispielzeile wäre also, wobei Temp das Ziel ist:

cp -l --parents 'misc/feed.png' temp

Dadurch wird diese Struktur erstellt:

temp
  misc
    feed.png

Beachten Sie, dass sich das Ziel im selben Dateisystem wie die Quelle befinden muss, damit Hardlinks funktionieren.

Der nächste Schritt besteht darin, das Skript zu erstellen:

sed -e "s,^,cp -l --parents '," -e "s,$,' /some/where/temp," filelist > newfilelist

Angenommen, Sie haben bereits das leere Verzeichnis / some / where / temp erstellt, können Sie die Dateien folgendermaßen kopieren:

sh newfilelist 2> missing_files

Beachten Sie, wie Fehler enden missing_files. Der zusätzliche Vorteil dieses Ansatzes besteht darin, dass Sie eine Liste der Dateien aus der ursprünglichen Liste erhalten, die tatsächlich nicht vorhanden sind!

Nach dem Ausführen des Skripts enthält temp nur die Dateien, die in der Dateiliste enthalten sind, ohne jedoch etwas zu löschen und ohne zusätzlichen Speicherplatz zu beanspruchen. Wenn Sie mit dem Ergebnis zufrieden sind, können Sie alle Originaldateien einschließlich der Unterordner löschen.

Verschieben Sie abschließend die Dateien und Ordner von temporär an den ursprünglichen Speicherort zurück.

Für die 18.000 Dateien dauerte es nur wenige Sekunden.

Marlar
quelle
0

Sicher, einfach.

cd zum Verzeichnis.

Erstellen Sie ein temporäres Verzeichnis.

mv *.yourExlusionSelector.* ./temp
rm *
mv ./temp ./
rm -rf ./temp

getan.

paradisaeidae
quelle
Willkommen auf der Website. Während Ihr Ansatz funktioniert, wenn die Namen in der vom OP erwähnten Liste das Ergebnis eines einfachen Mustervergleichs sind - was durchaus der Fall sein kann -, beachten Sie bitte, dass das OP angegeben hat, dass die auszuschließenden Dateinamen in einer bestimmten Datei gespeichert sind. Möglicherweise möchten Sie Ihre Antwort erweitern, um die Ausschlussmuster aus dieser Datei zu lesen, anstatt sich auf ein statisches Muster zu verlassen oder möglicherweise mehrere Muster auf die Konsole zu kopieren.
AdminBee