Entfernen Sie alle Dateien / Verzeichnisse mit Ausnahme einer Datei

244

Ich habe ein Verzeichnis mit einer großen Anzahl von Dateien. Ich möchte alle Dateien außer file.txt löschen. Wie mache ich das?

Es gibt zu viele Dateien, um die unerwünschten Dateien einzeln zu entfernen, und ihre Namen sind zu unterschiedlich, um sie alle mit Ausnahme dieser einen Datei mit * zu entfernen.

Jemand schlug vor, mit

rm !(file.txt)

Aber es geht nicht. Es gibt zurück:

Badly placed ()'s 

Mein Betriebssystem ist Scientific Linux 6.

Irgendwelche Ideen?

Kantura
quelle
13
Bewegen Sie den einen, den Sie behalten möchten, dann rm die anderen?
Olivier Dulac
5
@OlivierDulac Sind wir die einzigen zwei Personen in dieser Frage, die die Frage nicht überdenken?
Shadur
1
@shadur: Nun, ich kann mich auf sie beziehen: Oneliner sind attraktiv ... ^^
Olivier Dulac
1
Danke, und ja, ich habe nach einer einzeiligen Lösung gesucht. Es ist zu zeitaufwändig, um Dateien zu verschieben, da ich dies ziemlich oft tun muss.
Kantura

Antworten:

288

POSIXly:

find . ! -name 'file.txt' -type f -exec rm -f {} +

entfernt alle regulären Dateien (rekursiv, einschließlich versteckter) mit Ausnahme von file.txt. Um Verzeichnisse zu entfernen, wechseln Sie -type fzu -type dund fügen Sie die -rOption zu hinzu rm.

Für bashdie Verwendung rm -- !(file.txt)müssen Sie extglob aktivieren :

$ shopt -s extglob 
$ rm -- !(file.txt)

(oder anrufend bash -O extglob)

Beachten Sie, dass extglobnur in der bashKorn-Shell-Familie funktioniert . Und die Verwendung rm -- !(file.txt)kann einen Argument list too longFehler verursachen.

In zshkönnen Sie das ^Muster mit aktiviertem extendedglob negieren :

$ setopt extendedglob
$ rm -- ^file.txt

oder mit der gleichen Syntax mit kshund bashmit Optionen ksh_globund no_bare_glob_qualaktiviert.

cuonglm
quelle
9
Die Angabe des Verzeichnisses hat sich bewährt (in diesem Fall vollständiger Pfad? Oder fügen Sie hier eine Warnung hinzu, dass dieser Befehl alle Dateien ab dem aktuellen Arbeitsverzeichnis löscht ?). Normalerweise schreibe ich auch ein Beispiel mit rmas echo rmund fordere die Leute auf, das Echo nur dann zu löschen, wenn sie wirklich sicher sind, dass es das tut, was sie wollen. Ansonsten +1 für die gründliche Antwort
Olivier Dulac
11
Ich würde -deletestattdessen vorschlagen -exec, kürzer und leichter zu merken
Izkata
6
@Izkata: -deletewird von POSIX nicht definiert.
Cuonglm
5
Wie kann ich eine Liste von Dateien ausschließen?
Meysam
5
@Meysam - siehe meine Antwort für eine Lösung, die eine Liste von Dateien behandelt. Andernfalls findkönnen Sie mit Gnoucs Lösung ! \( -name one_file -o -name two_file \)und so weiter vorgehen .
mikeserv
112

Eine andere Einstellung in eine andere Richtung (falls die Dateinamen keine Leerzeichen enthalten)

ls | grep -v file.txt | xargs rm

oder (funktioniert auch, wenn Dateinamen Leerzeichen enthalten)

ls | grep -v file.txt | parallel rm

von man grep:

 -v, --invert-match
          Invert the sense of matching, to select non-matching lines.  (-v is specified by POSIX)
Sebastian
quelle
2
Es wird nicht funktionieren, wenn Dateinamen ein Leerzeichen haben ...
Matteo
1
Ciao @ Matteo, es funktioniert auch für Dateien mit Leerzeichen, aber Sie müssen das Grep-Muster in Anführungszeichen setzen, z ls | grep -v "a file with spaces.bin" | xargs rm. Dies ist normale grepSyntax.
Sebastian
1
@Sebastian Das Problem ist nicht das grepaber rm. rmwird eine Liste von durch Leerzeichen getrennten Argumenten erhalten. Versuchen Sie touch 'a b'; touch 'c d'; ls | grep -v 'a b' | xargs rm: Sie erhalten rm: c: No such file or directoryundrm: d: No such file or directory
Matteo
1
Ja, das ist ein häufiges Problem. Siehe die Optionen -print0in findund -0in xargs. Hier ist eine Problemumgehung, die jedoch etwas umständlich ist. find . -maxdepth 1 -type f | grep -v 'a b' | tr '\n' '\0' | xargs -0 rm. Übrigens, Gnu parallelhandhabt das gut (ich benutze es fast immer als Ersatz für xargs:ls | grep -v 'a b' | parallel rm
Sebastian
1
Es sind nicht nur Leerzeichen, sondern auch Leerzeichen und Zeilenumbrüche, sondern auch Anführungszeichen (einfache, doppelte Anführungszeichen und Backslash) und Dateinamen, die mit beginnen -.
Stéphane Chazelas
32

Kopie pflegen, alles löschen, Kopie wiederherstellen:

{   rm -rf *
    tar -x
} <<TAR
$(tar -c $one_file)
TAR

In einer Zeile:

{ rm -rf *; tar -x; } <<< $(tar -c $one_file)

Das setzt aber eine Shell voraus, die Here-Strings unterstützt.

mikeserv
quelle
10
Das ist etwas umwerfend.
Vschum
2
@Derek - siehe bitte die Bearbeitung.
MikeServ
2
Aber wenn dies auf halbem Weg unterbrochen wird oder wenn etwas anderes schief geht, ist die Datei weg.
Kasperd
2
@kasperd - nein. Nur wenn die übergeordnete Shell zwischen der Ausführung rmund stirbt tar -x. rmkann so oft scheitern, wie es will.
mikeserv
2
Ist es nicht effizienter, es in ein anderes Verzeichnis zu verschieben und zurück zu verschieben? Wir müssen uns nicht mit dem Inhalt der Datei befassen, sondern nur mit ihrem Pfad.
Leonbloy
23

das überdenkt ihr alle.

cd ..
mv fulldir/file.txt /tmp/
rm -rf fulldir
mkdir fulldir
mv /tmp/file.txt fulldir/

Getan.

EDIT Eigentlich einfacher:

cd ..
ln fulldir/file.txt ./
rm -rf fulldir
mkdir -p fulldir
mv file.txt fulldir/
Shadur
quelle
3
Das ist das Gleiche, was meine Antwort tut. exce [pt verliert keine Berechtigungen für das Verzeichnis.
mikeserv
6
Wenn es sich um eine große Datei handelt, die sich auf einem anderen Dateisystem als / tmp befindet und Sie versuchen, alles aus dem Stammverzeichnis des Dateisystems nach unten zu entfernen, ist es möglicherweise nicht möglich, sie an einen sicheren Ort zu verschieben.
Johnny
1
Dies ist definitiv die einfachste Antwort.
Ben Liyanage
17

Auf meinem Scientific Linux 6-Betriebssystem funktioniert Folgendes:

shopt -s extglob
rm !(file.txt)

Ich habe auch Debian 32bit auf einer virtuellen Maschine installiert. Das Obige funktioniert nicht, aber das Folgende funktioniert:

find . -type f ! -name 'file.txt' -delete
Kantura
quelle
1
Du meinst, die findLösung, die ich gegeben habe, hat nicht funktioniert?
5.
"hat nicht funktioniert" oder "funktioniert nicht". Ja, Ihre Suchlösung funktioniert auch. Vielen Dank.
Kantura
1
Können Sie es näher erläutern? Wie "hat nicht funktioniert? Es entfernt keine anderen Dateien oder es entfernt file.txtoder sonst etwas?
cuonglm
Ich meinte, dass unter Debian "$ rm! (File.txt)" "Schlecht platzierte ()" zurückgibt. Also habe ich "$ shopt -s extglob" ausprobiert, aber es gab zurück: "shopt: Befehl nicht gefunden". Dann habe ich die "find" -Lösung ausprobiert. Es funktioniert.
Kantura
1
Oh, das funktioniert natürlich nicht. shoptist kein tcshBuiltin.
5.
10

Verwenden Sie rm !("file.txt")anstelle vonrm !(file.txt)

www.wishinfo.net
quelle
4
Das macht absolut keinen Unterschied. Das Problem hierbei war, dass das OP 1) keine Shell verwendet, die dieses Format unterstützt, und 2) dass Sie es auch in Bash aktivieren müssen shopt -s extglob. In jedem Fall hätte das Zitieren eines einfachen Dateinamens keinen Unterschied gemacht.
terdon
1
Wenn Sie
7

Um eine andere Antwort zu geben, können Sie das Standardverhalten verwenden, bei dem rmOrdner nicht gelöscht werden:

mkdir tmp && mv file.txt tmp  # create tmp dir and move files there
rm                            # delete all other files
mv tmp/* . && rm -rf tmp      # move all files back and delete tmp dir
Nithin
quelle
1

Ich finde, dass dieser Ansatz sehr einfach ist, funktioniert und keine speziellen Erweiterungen erfordert (die ich kenne!)

ls --hide=file.txt | xargs rm
2 Bits
quelle