Entfernen von Dateien mit einer bestimmten Erweiterung außer einer Datei vom Terminal

36

Ich muss alle Dateien mit der Erweiterung .gif entfernen, mit Ausnahme einer Datei mit dem Namen "filename.gif". Was ist der optimale Weg, um dies im Terminal zu tun?

Der Befehl rm *.gifentfernt alle GIF-Dateien einschließlich der Datei filename.gif.

Seth
quelle

Antworten:

66

Hier ist die einfache Lösung, die Sie wahrscheinlich verwenden sollten:

mv filename.gif filename.gif.keep
rm *.gif
mv filename.gif.keep filename.gif

Es gibt nichts Besonderes an der .keepErweiterung, dies macht es nur so, dass der Dateiname vorübergehend nicht endet .gif.

Wenn Sie die Datei nicht umbenennen müssen (und es Skriptsituationen gibt , in denen dies wichtig ist):

for X in *.gif; do
    if [ "$X" != "filename.gif" ]; then
        rm "$X"
    fi
done

Oder du kannst es kürzer schreiben:

for X in *.gif; do [ "$X" != "filename.gif" ] && rm "$X"; done

Sie können es vorziehen, findstattdessen zu verwenden ; Es ist sehr mächtig, man könnte es für lesbarer halten und es behandelt seltsame Dateinamen mit Zeichen wie *in ihnen besser .

find . -maxdepth 1 -not -name 'filename.gif' -name '*.gif' -delete

Ich habe den -notOperator aus Gründen der Lesbarkeit verwendet, aber wenn die POSIX- Konformität wichtig ist - wenn Sie GNU find nicht verwenden oder wenn dies für ein Skript gilt, das Sie an andere weitergeben oder auf einer Vielzahl von Systemen ausführen möchten - sollten Sie dies tun Verwenden Sie !stattdessen den Operator:

find . -maxdepth 1 ! -name 'filename.gif' -name '*.gif' -delete

Eine praktische Sache findist, dass Sie den Befehl für Groß- und Kleinschreibung leicht ändern können, so dass er Dateien mit folgenden Erweiterungen findet und löscht .GIF:

find . -maxdepth 1 -not -name 'filename.gif' -iname '*.gif' -delete

Bitte beachten Sie, dass ich -inameanstelle von -namefür das Suchmuster verwendet habe, es *.gifaber nicht verwendet habe filename.gif. Vermutlich wissen Sie genau, wie Ihre Datei heißt, und -inamedie alternative Groß- und Kleinschreibung wird nicht nur in der Erweiterung , sondern an einer beliebigen Stelle im Dateinamen berücksichtigt.

Alle diese Lösungen löschen nur Dateien, die sich unmittelbar im aktuellen Verzeichnis befinden . Sie löschen keine Dateien, die nicht im aktuellen Verzeichnis enthalten sind, und sie löschen keine Dateien, die sich in Unterverzeichnissen des aktuellen Verzeichnisses befinden.

Wenn Sie Dateien löschen möchten, die sich überall im aktuellen Verzeichnis befinden (dh auch in Unterverzeichnissen und Unterverzeichnissen dieser Unterverzeichnisse usw. - Dateien, die sich im aktuellen Verzeichnis oder in einem seiner Nachkommen befinden), verwenden Sie find ohne maxdepth -1 :

find . -not -name 'filename.gif' -name '*.gif' -delete

Sei vorsichtig damit!

Sie können mit auch andere Werte einstellen -maxdepth. Zum Beispiel, um Dateien im aktuellen Verzeichnis und seinen Kindern und Enkelkindern zu löschen, aber nicht tiefer:

find . -maxdepth 3 -not -name 'filename.gif' -name '*.gif' -delete

Stellen Sie nur sicher, dass Sie niemals -deletezuerst vor den anderen Ausdrücken stehen! Du wirst sehen, ich habe immer -deleteam Ende gesetzt. Wenn Sie es an den Anfang stellen, wird es zuerst ausgewertet, und alle Dateien unter .(einschließlich Dateien, die nicht auf enden, .gifund Dateien in tiefen Unterverzeichnissen von ... .) werden gelöscht!

Weitere Informationen finden Sie in den Handbuchseiten für bashund shund für die Befehle in diesen Beispielen verwendet: mv, rm, [, und (vor allem) find.

Eliah Kagan
quelle
1
Danke Eliah. Ich habe es ähnlich der ersten Methode gemacht, die Sie vorgeschlagen haben, dh die Datei in ein anderes Format zu konvertieren und sie zurück zu konvertieren. Aber das schien ein bisschen suboptimal und unsauber. Ich mag den zweiten Ansatz, den Sie und @efthialex vorgeschlagen haben, und ich verwende ihn jetzt. Vielen Dank!
1
@Marvis Ich bin froh, dass dies Ihren Bedürfnissen entsprach. Übrigens habe ich diese Antwort um Informationen zu einer anderen Methode (oder Methodenklasse) erweitert find, die sehr vielseitig einsetzbar ist.
Eliah Kagan
3
Danke für die ausführliche Antwort findist ja ein guter Fund.
1
+1 für die Commonsense-Antwort (Verschieben-Löschen-Verschieben).
tu-Reinstate Monica-dor duh
Nit-Pick: Wie geht man findmit seltsamen Dateinamen *besser um als mit der for-Schleife? Die for-Schleife behandelt Dateien mit *fine, da Sie doppelte Anführungszeichen verwendet haben.
Flimm
28

Wenn Sie bash(die Standard-Shell) verwenden, extglobkönnen Sie mit der Shell-Option eine erweiterte Pattern-Matching-Syntax verwenden. Verwenden Sie zum shoptAktivieren den integrierten Befehl:

shopt -s extglob

(Ich füge diese Zeile in meine .bashrcDatei ein.)

Unter anderem wird dem !()Bediener der Zugriff gewährt , der mit jedem Muster übereinstimmt, das sich nicht innerhalb der Parens befindet. Für Ihren Zweck:

rm !(filename).gif

Weitere Informationen finden Sie man bashunter "Pattern Matching".

eswald
quelle
1
Hervorragende Antwort! Es ist bash-spezifisch, eignet sich also möglicherweise nicht für portables Scripting, aber interaktive Aufgaben und sogar einige Skripte. Dies ist zweifellos der beste Weg. Es bringt die zusätzliche Komplexität mit sich, extglobdie aktiviert werden muss, aber nichts Schlimmes passiert, wenn es deaktiviert ist (in diesem Fall funktioniert dies einfach nicht). Und es scheint standardmäßig aktiviert zu sein, obwohl ich shopt -s extglobkeine meiner (oder der globalen) Konfigurationsdateien habe. Wenn es eine einfache Erklärung dafür gibt, warum es bei mir standardmäßig funktioniert, können Sie es zu dieser Antwort hinzufügen. oder ich poste eine neue Frage.
Eliah Kagan
13

Öffne ein Terminal mit Ctrl+ Alt+ Tund tippe:

find . -type f -name "*.gif" -and -not -name "filename.gif" -exec rm -vf {} \;

Der Unterschied zwischen -deleteund -exec rm -vf {} \;besteht darin, dass Sie mit der zweiten Option sehen können, welche Dateien aufgrund der -vMarkierung entfernt wurden. Das ist etwas, was mit dieser -deleteOption nicht möglich ist. ( -name -deleteGibt die Dateinamen aus, zeigt jedoch rman -v, ob jede Datei erfolgreich gelöscht wurde.)

Efthialex
quelle
1

Da ist die GLOBIGNOREVariable. Von man bash:

GLOBIGNORE
      A colon-separated list of patterns defining the set of filenames
      to be ignored by pathname expansion.  If a filename matched by a
      pathname expansion pattern also matches one of the  patterns  in
      GLOBIGNORE, it is removed from the list of matches.

Eine einfache Möglichkeit besteht darin, nur GLOBGINOREden fraglichen Dateinamen festzulegen:

$ touch {a,b,c,d}.png
$ echo *.png
a.png b.png c.png d.png
$ GLOBIGNORE=c.png
$ echo *.png
a.png b.png d.png

Also, in deinem Fall:

GLOBIGNORE=filename.gif; rm *.gif

Da es GLOBIGNOREMuster enthält, können Sie es natürlich immer dann verwenden, wenn Sie ein Muster ausschließen möchten:

$ GLOBIGNORE='[a-c].png'; echo *.png
d.png
$ touch bd.png; GLOBIGNORE='?.png'; echo *.png
bd.png

(?!) Ein Vorteil GLOBIGNOREist , dass Einstellung automatisch schließt .und.. aus wird abgestimmt, und es ermöglicht dotglob:

The  GLOBIGNORE shell variable may be used to restrict the set of file‐
names matching a pattern.  If GLOBIGNORE is set, each matching filename
that also matches one of the patterns in GLOBIGNORE is removed from the
list of matches.  The filenames ``.''  and ``..''  are  always  ignored
when  GLOBIGNORE is set and not null.  However, setting GLOBIGNORE to a
non-null value has the effect of enabling the dotglob shell option,  so
all other filenames beginning with a ``.''  will match.  To get the old
behavior of ignoring filenames beginning with a ``.'', make ``.*''  one
of  the  patterns  in  GLOBIGNORE.  The dotglob option is disabled when
GLOBIGNORE is unset.

So löschen Sie auf einfache Weise alle Dateien und Ordner in einem Verzeichnis:

GLOBIGNORE=.; rm -r *

Das *stimmt mit den Dateinamen überein, die mit ., da GLOBIGNOREaktiviert dotglob, beginnen , stimmt jedoch nicht mit .oder überein .., sodass Sie das übergeordnete Verzeichnis nicht auf diese Weise beeinflussen.

muru
quelle