Datei entfernen, aber alle Dateien in einer Liste ausschließen


16

Ich muss regelmäßig einen Ordner bereinigen. Ich bekomme eine Dateiliste, die Text enthält, welche Dateien erlaubt sind. Jetzt muss ich alle Dateien löschen, die nicht in dieser Datei sind.

Beispiel:

dont-delete.txt:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt

Mein Ordner "Bereinigen" enthält Folgendes als Beispiel:

ls /home/me/myfolder2tocleanup/:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt
this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Also sollten diese Dateien gelöscht werden:

this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Ich suche etwas, um einen Löschbefehl mit einer Option zum Ausschließen einiger von file bereitgestellter Dateien zu erstellen.


Ist das eine Hausaufgabe?
Mook765

Ich hoffe du bist nicht sein Lehrer. lol
Gujarat Santana

2
@gujarat Wir sind nicht frei Hausaufgaben Service, so ist der Kommentar gerechtfertigt. Was die Frage selbst betrifft, kann sie für andere nützlich sein, so dass sie bisher offen ist.
Sergiy Kolodyazhnyy

@ Serg Ich bin völlig einverstanden mit Ihnen
Gujarat Santana

Antworten:


8

Der rmBefehl ist auskommentiert, damit Sie überprüfen und sicherstellen können, dass er bei Bedarf funktioniert. Dann entkommentieren Sie diese Zeile einfach.

In diesem check directoryAbschnitt wird sichergestellt, dass Sie das Skript nicht versehentlich aus dem falschen Verzeichnis ausführen und die falschen Dateien löschen.

Sie können die echo deletingLinie entfernen, um im Hintergrund zu laufen.

#!/bin/bash

cd /home/me/myfolder2tocleanup/

# Exit if the directory isn't found.
if (($?>0)); then
    echo "Can't find work dir... exiting"
    exit
fi

for i in *; do
    if ! grep -qxFe "$i" filelist.txt; then
        echo "Deleting: $i"
        # the next line is commented out.  Test it.  Then uncomment to removed the files
        # rm "$i"
    fi
done

Ich habe Ihren Code bearbeitet, um unnötigen Gebrauch vonls und die nutzlose Erfassung der Ausgabe von zu vermeiden, grepwenn Sie nur wissen möchten, ob eine Übereinstimmung vorliegt oder nicht. Ich habe auch feste Zeichenfolgen verwendet, um Probleme zu vermeiden.
David Foerster

@DavidFoerster Danke für den Beitrag. Wenn Sie jedoch die whileSchleife in eine forSchleife geändert haben, haben Sie versehentlich die Einstellung iteration keyvon iauf geändert f. in der Deklaration, die den Code brach. Ich habe es repariert.
LD James

Ups, Kraft der Gewohnheit. Ich neige dazu, Shell-Variablennamen für Dateinamen als abzukürzen f. ;-P (… und +1 für deine Antwort, die ich vorher vergessen habe.)
David Foerster

10

Dieses Python-Skript kann dies tun:

#!/usr/bin/env python3
import os
no_remove = set()
with open('./dont-delete.txt') as f:
     for line in f:
         no_remove.add(line.strip())

for f in os.listdir('.'):
    if f not in no_remove:
        print('unlink:' + f ) 
        #os.unlink(f)

Ein wichtiger Teil ist das Auskommentieren der os.unlink()Funktion.

HINWEIS : Fügen Sie dieses Skript und dont-delete.txtzu Ihrem hinzu, dont-delete.txtsodass beide in der Liste enthalten sind, und bewahren Sie sie im selben Verzeichnis auf.


1
Ich habe Ihren Code geändert, um setanstelle einer Liste für O (1) anstelle von O (n) eine Suche im zweiten Teil zu verwenden.
David Foerster


1
@ stefan83: Python läuft genauso gut unter Windows.
David Foerster

3

Hier ist ein Einzeiler:

comm -2 -3 <(ls) <(sort dont_delete) | tail +2 | xargs -p rm
  1. ls druckt alle Dateien im aktuellen Verzeichnis (in sortierter Reihenfolge)
  2. sort dont_delete druckt alle Dateien, die nicht gelöscht werden sollen, in sortierter Reihenfolge
  3. Der <()Operator verwandelt eine Zeichenfolge in ein dateiähnliches Objekt
  4. Der commBefehl vergleicht zwei vorsortierte Dateien und druckt Zeilen aus, in denen sie sich unterscheiden
  5. Wenn Sie die -2 -3Flags verwenden comm, werden nur die Zeilen gedruckt, die in der ersten, nicht aber in der zweiten Datei enthalten sind. Dies ist die Liste der Dateien, die sicher gelöscht werden können
  6. Der tail +2Aufruf besteht lediglich darin, die Überschrift der commAusgabe zu entfernen , die den Namen der Eingabedatei enthält
  7. Nun erhalten wir eine Liste der Dateien, die standardmäßig gelöscht werden sollen. Wir leiten diese Ausgabe weiter, xargsum den Ausgabestream in eine Liste von Argumenten für zu verwandeln rm. Die -pOption xargsfordert vor der Ausführung eine Bestätigung an.

Vielen Dank für Ihre Hilfe, jetzt habe ich meine Lösung!
Stefan83

@gardenhead, ich habe Ihren Code müde gemacht, aber er entfernt alle Dateien im Verzeichnis und behält nur die erste und die letzte Datei in der Dont-Delete-Liste. Hast du eine Idee für dieses Problem? Danke im Voraus.
Negar

1

FWIW sieht es so aus, als ob Sie dies von Haus aus zshmit dem (+cmd)Glob-Qualifikator tun können .

Beginnen wir zur Veranschaulichung mit einigen Dateien

 % ls
bar  baz  bazfoo  keepfiles.txt  foo  kazoo

und eine Whitelist-Datei

 % cat keepfiles.txt
foo
kazoo
bar

Lesen Sie zuerst die Whitelist in ein Array:

 % keepfiles=( "${(f)$(< keepfiles.txt)}" )

oder vielleicht besser

 % zmodload zsh/mapfile
 % keepfiles=( ${(f)mapfile[./keepfiles.txt]} )

(das Äquivalent zu mapfilebashs builtin - oder seinem Synonym readarray). Jetzt können wir überprüfen, ob ein Schlüssel (Dateiname) im Array vorhanden ist, ${keepfiles[(I)filename]}der 0 zurückgibt, wenn keine Übereinstimmung gefunden wird:

 % print ${keepfiles[(I)foo]}
1
 % print ${keepfiles[(I)baz]}
0
 %

Wir können dies verwenden, um eine Funktion zu erstellen, die zurückgibt, truewenn $REPLYdas Array keine Übereinstimmungen für enthält :

% nokeep() { (( ${keepfiles[(I)$REPLY]} == 0 )); }

Schließlich verwenden wir diese Funktion als Qualifikationsmerkmal in unserem Befehl:

 % ls *(+nokeep)
baz  bazfoo  keepfiles.txt

oder in deinem Fall

 % rm -- *(+nokeep)

(Wahrscheinlich möchten Sie den Namen der Whitelist-Datei selbst zur Whitelist hinzufügen.)


0

Angenommen, Ihre Bash-Shell hat den extglob shoptWert on, dann ist dies eine etwas konservativere Alternative:

rm !($(tr \\n \| < keep.txt))

(... zu @ gardenheads sonst hervorragendem Kommunikationsvorschlag!)


0

Angenommen, Ihre Dateien enthalten keine Leerzeichen (Leerzeichen / Tabulatoren) in einer Datei namens list. Dann würden Sie Folgendes tun:

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \)

Fügen Sie einfach -deleteauf den Befehl über die Dateien zu löschen , die nicht in der existieren Liste Datei. Wenn Ihr Fund nicht über -deleteOption können Sie verwenden , rmmit , -execwie folgend:

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \) -exec echo rm {} \;

Oder stattdessen -execmit +Terminator verwenden.

find /path/to -type f \( ! -name "list" $(printf ' -a ! -name %s\n' $(< list)) \) -exec echo rm {} +

echo wird nur zum Trockenlaufen verwendet.


0

Es sei denn, die Ausgabe von ls /home/me/myfolder2tocleanup/überschreitet die maximale Grenze für Shell-Argumente ARG_MAX , die um ist 2MB für Ubuntu, würde ich folgendes vorschlagen.


Eine einzeilige Befehlsimplementierung, die die Aufgabe erledigt, wäre wie folgt:

  1. Kopieren Sie die dont-delete.txtDatei in das Verzeichnis mit den zu löschenden Dateien wie folgt:
cp dont-delete.txt /home/me/myfolder2tocleanup/
  1. cd in das Verzeichnis mit den zu löschenden Dateien wie folgt:
cd /home/me/myfolder2tocleanup/
  1. Führen Sie einen Probelauf durch, um den Befehl zu testen, und lassen Sie ihn die Namen der Dateien drucken, die er als gelöscht erkennt, ohne sie tatsächlich zu löschen.
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs echo | tr " " "\n"
  1. Wenn Sie mit der Ausgabe zufrieden sind, löschen Sie die Dateien, indem Sie den folgenden Befehl ausführen:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs rm

Erklärung:

  • ls -plistet alle Dateien und Verzeichnisse im aktuellen Verzeichnis auf und die Option -pfügt ein/ zu den Verzeichnisnamen hinzu.
  • grep -v /schließt Verzeichnisse aus, indem alle Elemente entfernt werden, /deren Namen ein enthalten.
  • sed 's/\<dont-delete.txt\>//g'schließt die dont-delete.txtDatei aus, sodass sie dabei nicht gelöscht wird.
  • sortwird, nur um sicherzugehen, die verbleibende Ausgabe von sortieren ls.
  • comm -3 - <(sort dont-delete.txt)sortiert die dont-delete.txtDatei, vergleicht sie mit der sortierten Ausgabe von lsund schließt Dateinamen aus, die in beiden vorhanden sind.
  • xargs rmentfernt alle verbleibenden Dateinamen in der bereits verarbeiteten Ausgabe von ls. Dieses Mittel alle Elemente im aktuellen Verzeichnis wird mit Ausnahme entfernt werden Verzeichnisse , Dateien , die in der dont-delete.txtDatei und diedont-delete.txt Datei selbst entfernt werden

Im Trockenlauf:

  • xargs echo druckt die zu entfernenden Dateien aus.
  • tr " " "\n" übersetzt Leerzeichen in neue Zeilen, um die Lesbarkeit zu verbessern.

-1

Mein Vorschlag ist:

sed -e 's/^/\.\//' dont-delete.txt > dont-delete-relative-path.txt
find . -type f -print | grep -Fxvf dont-delete-relative-path.txt | xargs -d'\n' rm

Update 2018-08-07

Beispiel:

1: mkdir /tmp/delete-example && cd /tmp/delete-example
2: touch a b c d
3: echo "./a\n./b\n./dont-delete.txt\n" > dont-delete.txt
4: find . -type f -print | grep -Fxvf dont-delete.txt | xargs -d'\n' rm

Beachten Sie nach Zeile 3, dass Sie die dont-delete.txtDatei mit dem Inhalt haben:

./a
./b
./dont-delete.txt

(Die Führung ./ist sehr wichtig )

Die Dateien cund dwerden gelöscht.


Ich habe es mit einer Textdatei versucht, deren Dateinamen durch einen Zeilenumbruch getrennt sind. Am Ende wurden alle Dateien im Verzeichnis gelöscht.
Jacques MALAPRADE

Ich denke, Ihre "Keep List" war falsch.
Nyxz

Ich habe eine Beispielverwendung hinzugefügt.
Nyxz
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.