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?

Kantura
la source
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é.

cuonglm
la source
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)
Sébastien
la source
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.

Mikeserv
la source
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/
Shadur
la source
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
Kantura
la source
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)

www.wishinfo.net
la source
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
Nithin
la source
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
2 bits
la source