Supprimer tous les fichiers / répertoires sauf un fichier


244

J'ai un répertoire contenant un grand nombre de fichiers. Je veux supprimer tous les fichiers à l'exception de fichier.txt. Comment puis-je faire cela?

Il y a trop de fichiers pour supprimer les fichiers indésirables individuellement et leurs noms sont trop divers pour pouvoir être * supprimés, à l'exception de ce fichier.

Quelqu'un a suggéré d'utiliser

rm !(file.txt)

Mais ça ne marche pas. Il retourne:

Badly placed ()'s 

Mon système d'exploitation est Scientific Linux 6.

Des idées?


13
déplace celui que tu veux garder, puis rm les autres?
Olivier Dulac

5
@OlivierDulac Sommes-nous les deux seules personnes dans cette question à ne pas trop penser à la question?
Shadur

1
@shadur: bon, je peux les comprendre: les oneliners sont attrayants ... ^^
Olivier Dulac

1
Merci, et oui je cherchais une solution sur une ligne. Continuer à déplacer des fichiers prend trop de temps, car je dois le faire assez souvent.
Kantura

Réponses:


288

POSIXly:

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

supprimera tous les fichiers normaux (récursivement, y compris ceux cachés) sauf file.txt. Pour supprimer des répertoires, passez -type fà -type det ajoutez une -roption à rm.

Dans bash, pour l'utiliser rm -- !(file.txt), vous devez activer extglob :

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

(ou en appelant bash -O extglob)

Notez que extglobne fonctionne que dans bashet la famille de shell Korn. Et utiliser rm -- !(file.txt)peut provoquer une Argument list too longerreur.

Dans zsh, vous pouvez utiliser ^pour annuler le motif avec extendedglob activé:

$ setopt extendedglob
$ rm -- ^file.txt

ou en utilisant la même syntaxe avec kshet bashavec les options ksh_globet no_bare_glob_qualactivé.


9
Spécifier le répertoire est une bonne pratique (chemin complet dans ce cas? Ou peut-être ajouter ici un avertissement indiquant que cette commande supprime tous les fichiers commençant par le répertoire de travail en cours ?). En général, j’écris aussi tous les exemples avec rmas echo rmet demande aux gens de ne supprimer l’écho que s’ils sont vraiment certains que cela fera ce qu’ils veulent. Autre que cela, +1 pour la réponse approfondie
Olivier Dulac

11
Je suggérerais -deleteau lieu de -exec, plus court et plus facile à retenir
Izkata

6
@Izkata: -deleten'est pas défini par POSIX.
cuonglm

5
Comment exclure une liste de fichiers?
Meysam

5
@Meysam - voir ma réponse pour une solution qui gérera une liste de fichiers. Sinon, avec la findsolution Gnouc, vous pouvez le faire ! \( -name one_file -o -name two_file \), etc.
mikeserv

112

Une autre prise dans une direction différente (si et seulement s'il n'y a pas d'espaces dans les noms de fichiers)

ls | grep -v file.txt | xargs rm

ou (fonctionne même s'il y a des espaces dans les noms de fichiers)

ls | grep -v file.txt | parallel rm

de man grep:

 -v, --invert-match
          Invert the sense of matching, to select non-matching lines.  (-v is specified by POSIX)

2
Cela ne fonctionnera pas si les noms de fichiers ont un espace ...
Matteo

1
Ciao @Matteo, cela fonctionne aussi pour les fichiers avec des espaces, mais vous devez entourer le motif grep par des guillemets, par exemple ls | grep -v "a file with spaces.bin" | xargs rm. C'est la grepsyntaxe normale .
Sébastien

1
@ Sebastian Le problème n'est pas le grepmais rm. rmobtiendra une liste d'arguments séparés par des espaces. Essayez touch 'a b'; touch 'c d'; ls | grep -v 'a b' | xargs rm: vous obtiendrez rm: c: No such file or directoryetrm: d: No such file or directory
Matteo le

1
Oui, c'est un problème commun. Voir les options -print0dans findet -0dans xargs. Voici une solution de contournement, mais elle est légèrement gênante. find . -maxdepth 1 -type f | grep -v 'a b' | tr '\n' '\0' | xargs -0 rm. Au fait, gnu s'en paralleloccupe bien (je l'utilise presque toujours comme substitut de xargs:ls | grep -v 'a b' | parallel rm
Sebastian le

1
Ce ne sont pas seulement des espaces, ce sont des espaces et des nouvelles lignes, mais aussi des caractères de citation (guillemets simples, doubles guillemets et barres obliques inversées) et des noms de fichiers commençant par -.
Stéphane Chazelas

32

Conservez une copie, supprimez tout, restaurez la copie:

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

En une ligne:

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

Mais cela nécessite un shell qui supporte ici les chaînes.


10
C'est un peu hallucinant.
vschum

2
@Derek - s'il vous plaît voir l'édition.
mikeserv

2
Mais si cela est interrompu à mi-chemin ou si quelque chose ne va pas, le fichier est parti.
kasperd

2
@ kasperd - non. Seulement si le shell parent meurt entre le moment où il s'exécute rmet tar -x. rmpeut échouer autant qu'il veut.
mikeserv

2
N'est-il pas plus efficace de le déplacer dans un autre répertoire et de le ramener? Nous n'avons pas besoin de nous occuper du contenu du fichier, mais seulement de son chemin.
leonbloy

23

vous pensez tous cela.

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

Terminé.

EDIT En fait, plus facile:

cd ..
ln fulldir/file.txt ./
rm -rf fulldir
mkdir -p fulldir
mv file.txt fulldir/

3
c'est la même chose que ma réponse fait. exce [pt ne perd aucune autorisation sur le répertoire.
mikeserv

6
S'il s'agit d'un fichier volumineux sur un système de fichiers distinct de / tmp et que vous essayez de tout supprimer de la racine du système de fichiers, vous ne pouvez pas le déplacer dans un endroit sûr.
Johnny

1
C'est certainement la réponse la plus simple.
Ben Liyanage

17

Sur mon système d'exploitation Scientific Linux 6, cela fonctionne:

shopt -s extglob
rm !(file.txt)

J'ai également installé Debian 32 bits sur une machine virtuelle. Ce qui précède ne fonctionne pas mais ce qui suit:

find . -type f ! -name 'file.txt' -delete

1
Vous voulez dire que la findsolution que j'ai donnée n'a pas fonctionné?
cuonglm

"n'a pas fonctionné" ou "ne fonctionne pas". Oui, votre solution de recherche fonctionne également. Merci.
Kantura

1
Pouvez-vous le rendre plus détaillé? Comment "n'a pas fonctionné? Il ne supprime pas d'autres fichiers, ou il a supprimé file.txtou quoi que ce soit d'autre?
jeudi

Je voulais dire que sur le système d'exploitation Debian, "$ rm! (Fichier.txt)" renvoie "Mal positionné ()". J'ai donc essayé "$ shopt -s extglob", mais il a renvoyé: "shopt: Command not found". Ensuite, j'ai essayé la solution "trouver". Ça marche.
Kantura

1
Oh, bien sûr que ça ne marche pas. shoptn'est pas un tcshconstruit.
jeudi

10

Utiliser rm !("file.txt")au lieu derm !(file.txt)


4
Cela ne fait absolument aucune différence. Le problème ici était que l'OP était 1) n'utilisant pas de shell prenant en charge ce format et 2) même en bash, vous devez l'activer avec shopt -s extglob. Dans tous les cas, citer un nom de fichier simple comme celui-ci n'aurait fait aucune différence.
terdon

1
Pour conserver uniquement le dossier utils, rm -rf! ("Utils")
James Jithin

7

Juste pour donner une réponse différente, vous pouvez utiliser le comportement par défaut de rmne pas supprimer les dossiers:

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

1

Je trouve que cette approche est très simple, fonctionne et ne nécessite aucune extension particulière (à ma connaissance!)

ls --hide=file.txt | xargs rm
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.