Unix - Dateien und Ordner ohne MUSTER löschen

10

Vor kurzem stand ich vor der Aufgabe, alle Dateien / Ordner in einem Verzeichnis zu löschen, mit Ausnahme derjenigen, die einem bestimmten Muster entsprechen. Also habe ich mir einen einzeiligen Unix-Befehl ausgedacht, um die Arbeit zu erledigen. Muss es nur eine Zeile sein? Ich nehme nicht an, aber so ist es definitiv cooler!

Obwohl das Problem ziemlich einfach ist, war ich ein wenig überrascht, wie komplex meine Lösung war. Hier ist der Befehl, den ich verwendet habe; HINWEIS: Dies ist eine schlechte Lösung, da keine Dateinamen mit Zeilenvorschubzeichen verarbeitet werden (was in meiner Situation keine Rolle spielte).

ls | grep -v PATTERN | xargs -n1 -IREPLACE rm -rf REPLACE

Ich habe den Befehl "find" nicht verwendet, da ich nicht in Ordner zurückkehren möchte, die mit PATTERN übereinstimmen. Betrachten Sie beispielsweise die folgende Dateistruktur:

file_foo.txt
first_dir
  |
  +--> contents
  +--> ...
foo_dir
  |
  +--> anotherfile.txt
  +--> morefiles.log
foo_file.txt
somefile.txt

Bei Verwendung des Musters "foo" dürfen nur "first_dir" (und natürlich der Inhalt) und "somefile.txt" ( nicht "anotherfile.txt" oder "morefiles.log") entfernt werden.

Frage: Gibt es bessere (elegantere und korrektere) Möglichkeiten, dies zu erreichen?


EDIT:
Ich wurde kürzlich darauf aufmerksam gemacht, dass "Finden" eine bessere Option sein kann:

find * -maxdepth 0 ! -name PATTERN -print0 | xargs -0n1 rm -rf

Diese Lösung behandelt Pfade mit Zeilenvorschubzeichen korrekt.

joxl
quelle
BEARBEITEN: Fehlerhaftes "the_dir" geändert, um "first_dir" zu korrigieren.
Joxl

Antworten:

10

Die folgenden Beispiele haben ein echoPräfix, damit Sie die Muster testen können, bevor Sie sie tatsächlich verwenden. Entfernen Sie die echo, um die zu aktivieren rm -rf. Ersetzen Sie rm -ri, um zur Bestätigung aufzufordern.

ksh hat eine negative Match-Erweiterung für sein Globbing:

# ksh
echo rm -rf !(*foo*)

Die gleiche Syntax ist in bash verfügbar, wenn Sie die extglobOption festlegen :

# bash
shopt -s extglob
echo rm -rf !(*foo*)

zsh hat hierfür eine eigene Syntax:

# zsh
setopt extended_glob
echo rm -rf ^*foo*

Es kann auch die Syntax im ksh- Stil verwenden:

# zsh: ksh-style glob syntax
setopt ksh_glob no_bare_glob_qual
echo rm -rf !(*foo*)

# zsh: ksh-style glob syntax, adapted for the default bare_glob_qual option
setopt ksh_glob bare_glob_qual
echo rm -rf (!(*foo*))
Chris Johnsen
quelle
Einfach, elegant, behandelt Zeilenvorschubzeichen (fast) eine Zeile (ich habe sie shopt -s extglobzu meiner .bashrc-Datei hinzugefügt , daher handelt es sich jetzt um einen Einzeiler ). Perfekt! Ich verspreche eine positive Abstimmung, sobald mein Ruf dies zulässt.
Joxl
7

Hier ist eine andere findLösung. Ich bin mir nicht sicher, ob dies einen wirklichen Vorteil gegenüber Ihrem hat, aber es braucht xargsund berücksichtigt nicht die seltene Möglichkeit, dass * auf zu viele Namen erweitert wird.

find . -maxdepth 1 ! -name PATTERN -type f -delete

Ich habe auch hinzugefügt -type f, damit nicht versucht wird, Verzeichnisse zu löschen.

Warnung: -deleteist mächtig. Ich habe einer meiner Testdateien 0 Berechtigungen erteilt und der obige Befehl hat sie ohne zu zögern gelöscht.

Garyjohn
quelle
6
Zuerst testen. Ersetzen Sie -deletedurch, um -printzu sehen, ob die gewünschten Dateien gefunden werden. Wenn alles in Ordnung ist, verwenden Sie den Befehlsverlauf (z. B. drücken Sie die Aufwärts-Taste), um zum vorherigen Befehl zurückzukehren und sicherzustellen, dass Sie mit denselben Suchfiltern arbeiten.
Doug Harris
Lesen Sie sorgfältig, beachten Sie, dass ich tun entfernen möchten Verzeichnisse. Und find -deletenicht leere Verzeichnisse werden nicht entfernt. Beachten Sie auch, dass find -maxdepth 1"." (aktuelles Verzeichnis) was wirklich schlecht ist. Obwohl ich Ihre Idee der Beseitigung xargsmag, könnte man find -execalternativ verwenden. find * -maxdepth 0 ! -name PATTERN -exec rm -rf '{}' \;
Joxl
0

Sie können stattdessen dieses Rebol-Skriptbeispiel http://askcodegeneration.com/keep-subversion/ verwenden, das viel verständlicher ist und Benutzerinteraktionen wie Ordnerauswahl und -bestätigung verwalten kann:

delete-file: func[file][
    if/else none? find file "/.svn/" [
        delete file
    ][
        print ["keeping" file]
    ]
]
dir: request-dir
ans: ask rejoin ["Do you confirm " dir "? (Yes): "]
if (ans = "Yes") [
    foreach-file dir :delete-file
]
Rebol Tutorial
quelle