Entfernen Sie alle Dateien außer einigen aus einem Verzeichnis

215

sudo rm -rWie kann ich bei der Verwendung alle Dateien löschen, mit Ausnahme der folgenden:

textfile.txt
backup.tar.gz
script.php
database.sql
info.txt
Masjoko
quelle
5
Klingt nach einer Frage an unix.stackexchange.com
Jason
1
Es gibt zwei Möglichkeiten, diese Frage zu lesen, und die vorhandenen Antworten decken beide Interpretationen ab: BEIDES: (a) Dateien mit den angegebenen Namen direkt im Zielverzeichnis beibehalten und - wie rm -rimpliziert - alles andere löschen, einschließlich Unterverzeichnisse - auch wenn sie enthalten Dateien mit den angegebenen Namen; ODER: (b) den gesamten Teilbaum des Zielverzeichnisses durchlaufen und in jedem Verzeichnis alle Dateien außer denen mit den aufgeführten Namen löschen.
mklement0

Antworten:

219
find [path] -type f -not -name 'textfile.txt' -not -name 'backup.tar.gz' -delete

Wenn Sie -type ffind nicht angeben , werden auch Verzeichnisse aufgelistet, die Sie möglicherweise nicht möchten.


Oder eine allgemeinere Lösung mit der sehr nützlichen Kombination find | xargs:

find [path] -type f -not -name 'EXPR' -print0 | xargs -0 rm --

Löschen Sie beispielsweise alle Nicht-TXT-Dateien im aktuellen Verzeichnis:

find . -type f -not -name '*txt' -print0 | xargs -0 rm --

Die Kombination print0und -0wird benötigt, wenn in einem der Dateinamen Leerzeichen vorhanden sind, die gelöscht werden sollen.

awi
quelle
2
es scheint, dass xargs eine Textgrößenbeschränkung hat
frazras
26
um mehrere Muster zu löschen:find . -type f -not -name '*ignore1' -not -name '*ignore2' | xargs rm
Siwei Shen 申思维
5
was ist mit Verzeichnissen? Es werden alle Dateien gelöscht, aber werden Ordner entfernt?!
Orezvani
31
Anstelle von "| xargs rm" verwendet find einen -delete-Parameter.
Emil Stenström
1
stattdessen -print0 | xargs -0 rm --können Sie nur-delete
Andy
150
rm !(textfile.txt|backup.tar.gz|script.php|database.sql|info.txt)

Das Extglob (Extended Pattern Matching) muss in BASH aktiviert sein (falls es nicht aktiviert ist):

shopt -s extglob
pl1nk
quelle
2
Ich erhalte "Syntaxfehler in der Nähe eines unerwarteten Tokens" ("), wenn ich das tue shopt -s extglob; rm -rf !(README|LICENSE). Irgendeine Idee warum?
Dennis
@nic Ich erinnere mich nicht, sorry. Ich habe wahrscheinlich finddie -deleteOption verwendet.
Dennis
Dies ist die beste Lösung für mich und sie funktioniert standardmäßig auf meinem Ubuntu 12.04.4 LTS, ohne dass shopt
xtian
3
@nic, @Dennis: Der Syntaxfehler deutet darauf hin, dass etwas ANDERES als bashverwendet wurde, z. B. dashwo extglobes nicht unterstützt wird. In einer interaktiven bash Shell funktioniert der Befehl jedoch AUCH aus verschiedenen Gründen nicht wie angegeben. Kurz gesagt: shopt -s extglobBY ITSELF ausführen ; EINMAL ERFOLGREICH AKTIVIERT (überprüfen mit shopt extglob), ausführen rm -rf !(README|LICENSE). (Obwohl extglobes noch nicht aktiviert ist, !(...)wird es durch Verlaufserweiterung ausgewertet , BEVOR die Befehle ausgeführt werden. Da dies wahrscheinlich fehlschlägt, wird NIEMALS der Befehl ausgeführt und extglobnie
aktiviert
2
@nic, @Dennis, @ mklement0: Ich hatte das gleiche Problem mit "Syntaxfehler in der Nähe eines unerwarteten Tokens (", wenn der Befehl in einer .sh-Datei (#! / bin / bash) ausgeführt wurde, aber nicht, als ich ihn in einem Befehl ausführte Zeilenschnittstelle. Es stellte sich heraus, dass shopt -s extglobich zusätzlich zu der Ausführung in der Befehlszeilenschnittstelle diese in meiner Skriptdatei erneut ausführen musste. Dies löste das Problem für mich.
Ahresse
41

find . | grep -v "excluded files criteria" | xargs rm

Dadurch werden alle Dateien im aktuellen Verzeichnis aufgelistet, dann alle Dateien aufgelistet, die nicht Ihren Kriterien entsprechen (achten Sie darauf, dass sie mit den Verzeichnisnamen übereinstimmen), und sie werden dann entfernt.

Update : Wenn Sie basierend auf Ihrer Bearbeitung wirklich alles aus dem aktuellen Verzeichnis löschen möchten, außer den von Ihnen aufgelisteten Dateien, kann dies verwendet werden:

mkdir /tmp_backup && mv textfile.txt backup.tar.gz script.php database.sql info.txt /tmp_backup/ && rm -r && mv /tmp_backup/* . && rmdir /tmp_backup

Es wird ein Sicherungsverzeichnis erstellt /tmp_backup(Sie haben Root-Rechte, oder?), Die aufgelisteten Dateien in dieses Verzeichnis verschieben, alles im aktuellen Verzeichnis rekursiv löschen (Sie wissen, dass Sie sich im richtigen Verzeichnis befinden, oder?), Verschieben zurück zum aktuellen Verzeichnis alles aus /tmp_backupund schließlich löschen /tmp_backup.

Ich wähle das Sicherungsverzeichnis im Stammverzeichnis aus, denn wenn Sie versuchen, alles rekursiv aus dem Stammverzeichnis zu löschen, hat Ihr System große Probleme.

Sicher gibt es elegantere Möglichkeiten, dies zu tun, aber diese ist ziemlich einfach.

Darioo
quelle
2
Funktioniert auch sehr gut mit egrep, zB zum Reinigen von Latex-Zwischenfeilen:find . | egrep -v "\.tex|\.bib" | xargs rm
mtsz
erstaunlicher Befehl! Zum Entfernen von Verzeichnissen wechseln Sie einfach zu rm -r
Aris
1
Wenn Sie einfach alles im aktuellen Verzeichnis außer den ausgeschlossenen Kriterien löschen möchten: Funktioniert find . -maxdepth 1 | grep -v "exclude these" | xargs rm -rviel schneller, da kein unnötiger Drilldown in Verzeichnisse erforderlich ist.
Billynoah
Re- findPipeline: Abgesehen von Effizienzproblemen (3 Befehle werden für das verwendet, was findalleine möglich ist) funktioniert dies bei Dateinamen mit eingebetteten Leerzeichen nicht wie beabsichtigt und löscht möglicherweise die falschen Dateien.
mklement0
Ein Befehl, der "zu speichernde" Dateien an einem anderen Speicherort ( /tmp_backup) speichert, endet nicht gut, wenn er unterbrochen wird. Aus Sicht des Benutzers sind alle Dateien verschwunden, es sei denn, er weiß, wo er sie suchen muss, um sie zurückzubekommen . Aus diesem Grund bin ich nicht für diese Art von Strategie.
Craig McQueen
20

Angenommen, Dateien mit diesen Namen befinden sich an mehreren Stellen in der Verzeichnisstruktur und Sie möchten alle beibehalten:

find . -type f ! -regex ".*/\(textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt\)" -delete
Bis auf weiteres angehalten.
quelle
19

Sie können die Umgebungsvariable GLOBIGNORE in Bash verwenden.

Angenommen, Sie möchten alle Dateien außer PHP und SQL löschen, dann können Sie Folgendes tun:

export GLOBIGNORE=*.php:*.sql
rm *
export GLOBIGNORE=

Wenn Sie GLOBIGNORE so einstellen, werden PHP und SQL von Platzhaltern ignoriert, die wie "ls *" oder "rm *" verwendet werden. Wenn Sie also nach dem Festlegen der Variablen "rm *" verwenden, werden nur die Dateien txt und tar.gz gelöscht.

theharshest
quelle
6
+1; Eine einfachere Alternative zum Festlegen und Wiederherstellen der GLOBIGNOREVariablen ist die Verwendung einer Unterschale:(GLOBIGNORE='*.php:*.sql'; rm *)
mklement0
19

Ich bevorzuge die Verwendung einer Unterabfrageliste:

rm -r `ls | grep -v "textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt"`

-v, --invert-match wählt nicht übereinstimmende Zeilen aus

\| Separator

Nick Tsai
quelle
1
Elegante Lösung
Joshua Salazar
9

Da hat es niemand erwähnt:

  • Kopieren Sie die Dateien, die Sie nicht löschen möchten, an einen sicheren Ort
  • Löschen Sie alle Dateien
  • Verschieben Sie die kopierten Dateien wieder an ihren Platz
gniourf_gniourf
quelle
2
Dies ist komplizierter, da Sie sich um die Berechtigungen kümmern müssen, nachdem Sie sie zurückkopiert haben.
Romulus
2
@RemusAvram Sie können geeignete Switches mit cpoder verwenden rsync, um Berechtigungen beizubehalten . Auf jeden Fall ist dies nur eine alternative Methode (als Vorschlag angegeben), die hier ihren Platz als Antwort auf das OP hat.
gniourf_gniourf
Dies ist in vielen Situationen, in denen die aufzubewahrenden Dateien aktiv von anderen Prozessen verwendet werden, möglicherweise nicht angemessen. Es ist auch umständlich, dass die zu entfernenden Dateien nur eine kleine Teilmenge sind und eine große Anzahl von Dateien beteiligt ist.
Codeforester
@codeforester: sicher. Aber es gibt Situationen, in denen es angemessen ist ... tatsächlich verstehe ich den Sinn Ihres Kommentars nicht wirklich :).
gniourf_gniourf
6

Sie können eine for-Schleife dafür schreiben ...%)

for x in *
do
        if [ "$x" != "exclude_criteria" ]
        then
                rm -f $x;
        fi
done;
Mischunika
quelle
6

Ein bisschen spät für das OP, aber hoffentlich nützlich für alle, die viel später per Google hierher kommen ...

Ich fand die Antwort von @awi und den Kommentar zu -delete von @Jamie Bullock wirklich nützlich. Ein einfaches Dienstprogramm, mit dem Sie dies in verschiedenen Verzeichnissen tun können, wobei jedes Mal unterschiedliche Dateinamen / -typen mit minimaler Eingabe ignoriert werden:

rm_except (oder wie auch immer Sie es benennen möchten)

#!/bin/bash

ignore=""

for fignore in "$@"; do
  ignore=${ignore}"-not -name ${fignore} "
done

find . -type f $ignore -delete

zB um alles außer Textdateien und foo.bar zu löschen:

rm_except *.txt foo.bar 

Ähnlich wie @mishunika, jedoch ohne die if-Klausel.

lhuber
quelle
5

Wenn Sie verwenden, zshwas ich sehr empfehlen.

rm -rf ^file/folder pattern to avoid

Mit extended_glob

setopt extended_glob
rm -- ^*.txt
rm -- ^*.(sql|txt)
Sudo Bangbang
quelle
4

Der Versuch funktionierte mit:

rm -r !(Applications|"Virtualbox VMs"|Downloads|Documents|Desktop|Public)

aber Namen mit Leerzeichen sind (wie immer) hart. Versuchte auch mit Virtualbox\ VMsstattdessen die Anführungszeichen. Es löscht immer dieses Verzeichnis ( Virtualbox VMs).

Juanfal
quelle
Funktioniert NICHT für Dateien. rm !(myfile.txt)entfernt alle einschließlichmyfile.txt
khaverim
sollte shopt -s extglobzuerst ausgeführt werden, wie @ pl1nk hervorhob
zhuguowei
Ja, es ist sehr wichtig , aktivieren Sie das Extended Globbing ( extglob ) in bash, ich dies in einer täglichen Routine Grundlage tue, über ein paar Macs in Labors: shopt -s extglobund dann cd /Users/alumno/und schließlich rm -rf !(Applications|Virtualbox*VMs|Downloads|Documents|Desktop|Public|Library) lesen , erweiterte Globbing hier
juanfal
4

Gerade:

rm $(ls -I "*.txt" ) #Deletes file type except *.txt

Oder:

rm $(ls -I "*.txt" -I "*.pdf" ) #Deletes file types except *.txt & *.pdf
Wyx
quelle
Dies wird nicht empfohlen. Das Parsen der lsAusgabe kann zu einem Problem werden. Siehe hier für weitere Informationen.
RJ
2

Machen Sie die Dateien unveränderlich. Nicht einmal root darf sie löschen.

chattr +i textfile.txt backup.tar.gz script.php database.sql info.txt
rm *

Alle anderen Dateien wurden gelöscht.
Schließlich können Sie sie veränderlich zurücksetzen.

chattr -i *
L'alligator des abers
quelle
0

Dies ähnelt dem Kommentar von @ siwei-shen, aber Sie benötigen das -oFlag, um mehrere Muster zu erstellen . Die -oFlagge steht für 'oder'

find . -type f -not -name '*ignore1' -o -not -name '*ignore2' | xargs rm

Daniel Chen
quelle
0

Sie können dies mit zwei Befehlssequenzen tun. Definieren Sie zunächst ein Array mit dem Namen der Dateien, die Sie nicht ausschließen möchten:

files=( backup.tar.gz script.php database.sql info.txt )

Durchlaufen Sie anschließend alle Dateien in dem Verzeichnis, das Sie ausschließen möchten, und prüfen Sie, ob sich der Dateiname in dem Array befindet, das Sie nicht ausschließen möchten. Wenn dies nicht der Fall ist, löschen Sie die Datei.

for file in *; do
  if [[ ! " ${files[@]} " ~= "$file"  ]];then
    rm "$file"
  fi
done
Leonardo Hermoso
quelle
Der Regex-Vergleich ist falsch - es werden alle Dateien beibehalten, deren Name eine Teilzeichenfolge einer der geschützten Dateien ist (obwohl die umgebenden Leerzeichen dies etwas abschwächen; Dateinamen können jedoch Leerzeichen enthalten). Sie sollten ein Array aller Dateien sammeln, dann die Dateien subtrahieren, die Sie von diesem Array ausschließen möchten, und dann die verbleibenden Dateien entfernen.
Tripleee
-1

Da dies noch niemand erwähnt hat, in einem bestimmten Fall:

OLD_FILES=`echo *`
... create new files ...
rm -r $OLD_FILES

(oder einfach rm $OLD_FILES)

oder

OLD_FILES=`ls *`
... create new files ...
rm -r $OLD_FILES

Möglicherweise müssen Sie Folgendes verwenden, shopt -s nullglobwenn einige Dateien vorhanden sind oder nicht:

SET_OLD_NULLGLOB=`shopt -p nullglob`
shopt -s nullglob
FILES=`echo *.sh *.bash`
$SET_OLD_NULLGLOB

ohne nullglob echo *.sh *.bashkann Ihnen "a.sh b.sh * .bash" geben.

(Nachdem ich das alles gesagt habe, bevorzuge ich selbst diese Antwort , obwohl sie unter OSX nicht funktioniert.)

18446744073709551615
quelle
Die bestehende Antwort von Leonardo Hermoso macht dies mit weniger Fehlern. Dies schlägt bei Dateinamen mit Leerzeichen fehl. Die Verwendung eines Arrays löst dieses spezielle Problem auf elegante Weise (und vermeidet auch weniger entscheidend die Verwendung von Großbuchstaben für seine privaten Variablen, was im Allgemeinen vermieden werden sollte).
Tripleee
-3

Anstatt einen direkten Befehl auszuführen, verschieben Sie bitte die erforderlichen Dateien in ein temporäres Verzeichnis außerhalb des aktuellen Verzeichnisses. Löschen Sie dann alle Dateien mit rm *oder rm -r *.

Verschieben Sie dann die erforderlichen Dateien in das aktuelle Verzeichnis.

Ajeesh
quelle
Dies ist in vielen Situationen, in denen die aufzubewahrenden Dateien aktiv von anderen Prozessen verwendet werden, möglicherweise nicht angemessen. Es ist auch umständlich, dass die zu entfernenden Dateien nur eine kleine Teilmenge sind und eine große Anzahl von Dateien beteiligt ist.
Codeforester
-3

Entfernen Sie alles außer file.name:

ls -d /path/to/your/files/* |grep -v file.name|xargs rm -rf
mik-mak
quelle
2
Dies schlägt schrecklich fehl und kann zu Datenverlust führen, wenn Ihr Verzeichnis / Ihre Dateien Leerzeichen oder ungewöhnliche Zeichen enthalten. Sie sollten niemals ls analysieren. mywiki.wooledge.org/ParsingLs
RJ