Supprimer tous les fichiers sauf certains d'un répertoire

215

Lors de l'utilisation sudo rm -r, comment puis-je supprimer tous les fichiers, à l'exception des éléments suivants:

textfile.txt
backup.tar.gz
script.php
database.sql
info.txt
masjoko
la source
5
Cela
Jason
1
Il y a 2 façons de lire cette question, et les réponses existantes couvrent les deux interprétations: SOIT: (a) conserver les fichiers avec les noms spécifiés directement situés dans le répertoire cible et - comme cela rm -rimplique - supprimer tout le reste, y compris les sous - répertoires - même s'ils contiennent fichiers avec les noms spécifiés; OU: (b) parcourir l'intégralité de la sous-arborescence du répertoire cible et, dans chaque répertoire, supprimer tous les fichiers sauf ceux portant les noms répertoriés.
mklement0

Réponses:

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

Si vous ne spécifiez pas, -type ffind répertorie également les répertoires, que vous ne souhaitez peut-être pas.


Ou une solution plus générale utilisant la combinaison très utile find | xargs:

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

par exemple, supprimez tous les fichiers non txt du répertoire courant:

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

La combinaison print0et -0est nécessaire s'il y a des espaces dans l'un des noms de fichiers qui doivent être supprimés.

awi
la source
2
il semble que xargs ait une limite de taille de texte
frazras
26
pour supprimer plusieurs motifs:find . -type f -not -name '*ignore1' -not -name '*ignore2' | xargs rm
Siwei Shen 申思维
5
qu'en est-il des répertoires? il supprimera tous les fichiers, mais supprimera-t-il les dossiers?!
orezvani
31
Au lieu de "| xargs rm", find prend un paramètre -delete.
Emil Stenström
1
au lieu de -print0 | xargs -0 rm --vous pouvez simplement-delete
Andy
151
rm !(textfile.txt|backup.tar.gz|script.php|database.sql|info.txt)

L'extglob (Extended Pattern Matching) doit être activé dans BASH (s'il n'est pas activé):

shopt -s extglob
pl1nk
la source
2
J'obtiens "une erreur de syntaxe près du jeton inattendu` ('"quand je le fais shopt -s extglob; rm -rf !(README|LICENSE). Une idée pourquoi?
Dennis
@nic Je ne me souviens pas, désolé. J'ai probablement fini par utiliser findl' -deleteoption.
Dennis
C'est la meilleure solution pour moi et elle fonctionne par défaut sur mon Ubuntu 12.04.4 LTS sans avoir besoin de shopt
xtian
3
@nic, @Dennis: L'erreur de syntaxe suggère que quelque chose AUTRE que ce qui a bashété utilisé, par exemple dash, où extglobn'est pas pris en charge. Cependant, dans un shell interactif bash , la commande ne fonctionnera AUSSI PAS comme indiqué, mais pour des raisons différentes. En bref: exécutez shopt -s extglobBY ITSELF; UNE FOIS AVEC SUCCÈS ACTIVÉ (vérifier avec shopt extglob), exécutez rm -rf !(README|LICENSE). (Bien qu'il extglobne soit pas encore activé, il !(...)est évalué par l'expansion de l'historique AVANT l'exécution des commandes; car cela échoue probablement, AUCUNE commande n'est exécutée et extglobn'est jamais activée.)
mklement0
2
@nic, @Dennis, @ mklement0: J'ai eu le même problème avec "erreur de syntaxe près d'un jeton inattendu (" lors de l'exécution de la commande dans un fichier .sh (#! / bin / bash) mais pas lorsque je l'exécutais dans une commande interface de ligne. Il s'est avéré qu'en plus de l' shopt -s extglobexécution dans l'interface de ligne de commande, je devais la relancer dans mon fichier de script. Cela a résolu le problème pour moi.
Ahresse
41

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

Cela listera tous les fichiers du répertoire courant, puis listera tous ceux qui ne correspondent pas à vos critères (attention à ce qu'ils correspondent aux noms de répertoires) puis les supprimera.

Mise à jour : en fonction de votre modification, si vous voulez vraiment tout supprimer du répertoire actuel, à l'exception des fichiers que vous avez répertoriés, cela peut être utilisé:

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

Il va créer un répertoire de sauvegarde /tmp_backup(vous avez les privilèges root, non?), Déplacer les fichiers que vous avez listés dans ce répertoire, supprimer récursivement tout dans le répertoire courant (vous savez que vous êtes dans le bon répertoire, n'est-ce pas?), Déplacer revenir au répertoire actuel tout de /tmp_backupet enfin, supprimer /tmp_backup.

Je choisis que le répertoire de sauvegarde soit en root, car si vous essayez de tout supprimer récursivement de root, votre système aura de gros problèmes.

Il existe certainement des façons plus élégantes de le faire, mais celui-ci est assez simple.

darioo
la source
2
Fonctionne également très bien avec egrep, par exemple pour nettoyer les fichiers intermédiaires en latex:find . | egrep -v "\.tex|\.bib" | xargs rm
mtsz
commande incroyable! pour supprimer les répertoires, passez simplement à rm -r
Aris
1
Si vous souhaitez simplement supprimer tout ce qui se trouve dans le répertoire actuel, à l'exception des critères exclus: find . -maxdepth 1 | grep -v "exclude these" | xargs rm -rfonctionne beaucoup plus rapidement car il n'a pas besoin d'explorer les répertoires inutilement.
billynoah
Re findpipeline: les problèmes d'efficacité mis à part (3 commandes sont utilisées pour ce qui findpourrait faire seul), cela ne fonctionnera pas comme prévu avec les noms de fichiers avec des espaces intégrés et supprimera potentiellement les mauvais fichiers.
mklement0
Une commande qui stocke des fichiers «à enregistrer» dans un autre emplacement ( /tmp_backup) ne se termine pas bien si elle est interrompue - du point de vue de l'utilisateur, tous les fichiers ont disparu, à moins qu'ils ne sachent où aller les chercher pour les récupérer . Pour cette raison, je ne suis pas en faveur de ce type de stratégie.
Craig McQueen
20

En supposant que les fichiers portant ces noms existent à plusieurs endroits de l'arborescence de répertoires et que vous souhaitez tous les conserver:

find . -type f ! -regex ".*/\(textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt\)" -delete
En pause jusqu'à nouvel ordre.
la source
19

Vous pouvez utiliser la variable d'environnement GLOBIGNORE dans Bash.

Supposons que vous vouliez supprimer tous les fichiers sauf php et sql, vous pouvez alors faire ce qui suit -

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

Définir GLOBIGNORE comme ceci ignore le php et le sql des caractères génériques utilisés comme "ls *" ou "rm *". Ainsi, l'utilisation de "rm *" après avoir défini la variable supprimera uniquement le fichier txt et tar.gz.

theharshest
la source
6
+1; Une alternative plus simple à la définition et à la restauration de la GLOBIGNOREvariable consiste à utiliser un sous-shell:(GLOBIGNORE='*.php:*.sql'; rm *)
mklement0
19

Je préfère utiliser la liste de sous-requêtes:

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

-v, --invert-match sélectionner les lignes non correspondantes

\| Séparateur

Nick Tsai
la source
1
Solution élégante
Joshua Salazar
9

Puisque personne ne l'a mentionné:

  • copiez les fichiers que vous ne voulez pas supprimer dans un endroit sûr
  • supprimer tous les fichiers
  • remettre les fichiers copiés en place
gniourf_gniourf
la source
2
C'est plus compliqué car vous devez prendre soin des autorisations après les avoir recopiées.
Romulus
2
@RemusAvram Vous pouvez utiliser les commutateurs appropriés avec cpou rsyncpour conserver les autorisations. Quoi qu'il en soit, ce n'est qu'une méthode alternative (donnée à titre de suggestion) qui a sa place ici, comme réponse au PO.
gniourf_gniourf
Cela peut ne pas être approprié dans de nombreuses situations où les fichiers à conserver sont activement utilisés par d'autres processus. Il est également encombrant que les fichiers à supprimer ne soient qu'un petit sous-ensemble et qu'un grand nombre de fichiers soient impliqués.
codeforester le
@codeforester: bien sûr. Mais il y a des situations où c'est approprié, cependant ... en fait, je ne vois pas vraiment le sens de votre commentaire :).
gniourf_gniourf
6

Vous pouvez écrire une boucle for pour cela ...%)

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

Un peu tard pour l'OP, mais j'espère utile pour tous ceux qui arriveront bien plus tard par google ...

J'ai trouvé la réponse de @awi et le commentaire sur -delete de @Jamie Bullock vraiment utiles. Un utilitaire simple pour que vous puissiez le faire dans différents répertoires en ignorant à chaque fois différents noms / types de fichiers avec un minimum de frappe:

rm_except (ou ce que vous voulez lui donner)

#!/bin/bash

ignore=""

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

find . -type f $ignore -delete

par exemple pour tout supprimer sauf pour les fichiers texte et foo.bar:

rm_except *.txt foo.bar 

Similaire à @mishunika, mais sans la clause if.

lhuber
la source
5

Si vous utilisez zshce que je recommande fortement.

rm -rf ^file/folder pattern to avoid

Avec extended_glob

setopt extended_glob
rm -- ^*.txt
rm -- ^*.(sql|txt)
sudo bangbang
la source
4

L'essayer a fonctionné avec:

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

mais les noms avec des espaces sont (comme toujours) difficiles. J'ai également essayé avec Virtualbox\ VMsles citations. Il supprime toujours ce répertoire ( Virtualbox VMs).

juanfal
la source
Ne fonctionne PAS pour les fichiers. rm !(myfile.txt)supprime tout, y comprismyfile.txt
khaverim
devrait s'exécuter en shopt -s extglobpremier comme @ pl1nk l'a souligné
zhuguowei
Oui, il est très important d'activer extension de englobement ( extglob ) en bash, je fais cela dans une base de routine quotidienne, sur quelques Mac dans les laboratoires: shopt -s extglobpuis cd /Users/alumno/et enfin rm -rf !(Applications|Virtualbox*VMs|Downloads|Documents|Desktop|Public|Library) Lire au sujet étendu englobement ici
juanfal
4

Juste:

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

Ou:

rm $(ls -I "*.txt" -I "*.pdf" ) #Deletes file types except *.txt & *.pdf
wyx
la source
Ce n'est pas recommandé. L'analyse de la lssortie peut devenir un problème. Voir ici pour des informations détaillées.
RJ
2

Rendez les fichiers immuables. Même root ne sera pas autorisé à les supprimer.

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

Tous les autres fichiers ont été supprimés.
Finalement, vous pouvez les réinitialiser mutables.

chattr -i *
L'alligator des abers
la source
0

Ceci est similaire au commentaire de @ siwei-shen mais vous avez besoin du -odrapeau pour faire plusieurs modèles. Le -odrapeau signifie «ou»

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

Daniel Chen
la source
0

Vous pouvez le faire avec deux séquences de commandes. Définissez d'abord un tableau avec le nom des fichiers que vous ne souhaitez pas exclure:

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

Après cela, parcourez tous les fichiers du répertoire que vous souhaitez exclure, en vérifiant si le nom de fichier se trouve dans le tableau que vous ne souhaitez pas exclure; si ce n'est pas le cas, supprimez le fichier.

for file in *; do
  if [[ ! " ${files[@]} " ~= "$file"  ]];then
    rm "$file"
  fi
done
Leonardo Hermoso
la source
La comparaison des expressions rationnelles est incorrecte - elle préservera tout fichier dont le nom est une sous-chaîne de l'un des fichiers protégés (bien que les espaces environnants atténuent quelque peu cela; mais les noms de fichiers peuvent contenir des espaces). Vous devez collecter un tableau de tous les fichiers, puis soustraire les fichiers que vous souhaitez exclure de ce tableau, puis supprimer les fichiers restants.
tripleee
-1

Comme personne ne l'a encore mentionné, dans un cas particulier:

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

(ou tout simplement rm $OLD_FILES)

ou

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

Vous devrez peut-être utiliser shopt -s nullglobsi certains fichiers peuvent être présents ou absents:

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

sans nullglob, echo *.sh *.bashpeut vous donner "a.sh b.sh * .bash".

(Cela dit, je préfère moi-même cette réponse , même si elle ne fonctionne pas sous OSX)

18446744073709551615
la source
La réponse existante de Leonardo Hermoso le fait avec moins de bugs. Cela échouera pour les noms de fichiers avec des espaces; l'utilisation d'un tableau résout avec élégance ce problème particulier (et évite également de manière moins cruciale d'utiliser des majuscules pour ses variables privées, ce qui est généralement à éviter).
tripleee
-3

Plutôt que d'aller pour une commande directe, veuillez déplacer les fichiers requis vers le répertoire temporaire en dehors du répertoire actuel. Supprimez ensuite tous les fichiers à l'aide de rm *ou rm -r *.

Déplacez ensuite les fichiers requis vers le répertoire actuel.

Ajeesh
la source
Cela peut ne pas être approprié dans de nombreuses situations où les fichiers à conserver sont activement utilisés par d'autres processus. Il est également encombrant que les fichiers à supprimer ne soient qu'un petit sous-ensemble et qu'un grand nombre de fichiers soient impliqués.
codeforester le
-3

Supprimer tout exclure file.name:

ls -d /path/to/your/files/* |grep -v file.name|xargs rm -rf
mik-mak
la source
2
Cela échouera horriblement et pourrait entraîner une perte de données si votre répertoire / fichiers contient des espaces ou des caractères inhabituels. Vous ne devriez jamais analyser ls. mywiki.wooledge.org/ParsingLs
RJ