Supprimer des fichiers avec une certaine extension sauf un fichier du terminal

36

Je dois supprimer tous les fichiers avec l'extension .gif, à l'exception d'un fichier portant le nom "filename.gif". Quel est le moyen optimal de procéder dans un terminal?

La commande rm *.gifsupprime tous les fichiers gif, y compris le fichier filename.gif.

Seth
la source

Réponses:

66

Voici la solution simple que vous devriez probablement utiliser:

mv filename.gif filename.gif.keep
rm *.gif
mv filename.gif.keep filename.gif

L' .keepextension n'a rien de spécial , cela permet simplement d'éviter que le nom du fichier ne se termine temporairement .gif.

Si vous ne devez pas renommer le fichier (et qu'il existe des situations de script dans lesquelles cela est important):

for X in *.gif; do
    if [ "$X" != "filename.gif" ]; then
        rm "$X"
    fi
done

Ou vous pouvez l'écrire plus court comme ceci:

for X in *.gif; do [ "$X" != "filename.gif" ] && rm "$X"; done

Vous préférerez peut-être utiliser à la findplace; il est très puissant, vous pouvez le considérer plus lisible, et il vaut mieux les poignées des noms de fichiers étranges avec des personnages comme *en eux .

find . -maxdepth 1 -not -name 'filename.gif' -name '*.gif' -delete

J'ai utilisé l' -notopérateur pour plus de lisibilité, mais si la conformité POSIX est importante - si vous n'utilisez pas GNU Find ou si c'est pour un script que vous souhaitez redistribuer à d'autres ou exécuter sur différents systèmes - vous devez utilisez l' !opérateur à la place:

find . -maxdepth 1 ! -name 'filename.gif' -name '*.gif' -delete

Un avantage pratique findest que vous pouvez facilement modifier la commande pour tenir compte de la casse, afin de rechercher et de supprimer les fichiers portant l’extension suivante .GIF:

find . -maxdepth 1 -not -name 'filename.gif' -iname '*.gif' -delete

Veuillez noter que j'ai utilisé -inameà la place de -namepour le modèle de recherche *.gifmais je ne l' ai pas utilisé pour filename.gif. Vraisemblablement, vous savez exactement comment votre fichier s'appelle et -inamecorrespondra à une casse supplémentaire non seulement dans l' extension , mais n'importe où dans le nom du fichier.

Toutes ces solutions suppriment uniquement les fichiers résidant immédiatement dans le répertoire en cours . Ils ne suppriment pas les fichiers ne figurant pas dans le répertoire en cours, ni les fichiers résidant dans des sous-répertoires du répertoire en cours.

Si vous souhaitez supprimer des fichiers situés partout dans le répertoire actuel (c'est-à-dire y compris dans des sous-répertoires et des sous-répertoires de ces sous-répertoires, et ainsi de suite - fichiers contenus dans le répertoire actuel ou l'un de ses descendants), utilisez find sans maxdepth -1 :

find . -not -name 'filename.gif' -name '*.gif' -delete

Soyez prudent avec ça!

Vous pouvez également définir d'autres valeurs avec -maxdepth. Par exemple, pour supprimer des fichiers du répertoire actuel et de ses enfants et petits-enfants, mais pas plus profondément:

find . -maxdepth 3 -not -name 'filename.gif' -name '*.gif' -delete

Assurez-vous juste de ne jamais mettre en -deletepremier, avant les autres expressions! Vous verrez que j'ai toujours mis -deleteà la fin. Si vous le mettez au début, il sera évalué en premier et tous les fichiers sous .(y compris les fichiers ne se terminant pas .gifet les fichiers dans les sous-sous-sous-répertoires profonds de .) seront supprimés!

Pour plus d' informations , consultez les pages de manuel pour bashet shet pour les commandes utilisées dans ces exemples: mv, rm, [et (surtout) find.

Eliah Kagan
la source
1
Merci Eliah. Je l’ai fait de la même manière que la première méthode que vous avez suggérée, c’est-à-dire convertir le fichier dans un format différent et le reconvertir. Mais cela semblait un peu sous-optimal et malpropre. J'aime la deuxième approche que vous et @efthialex avez suggérée et que je l'utilise maintenant. Merci!
1
@Marvis Je suis heureux que cela réponde à vos besoins. A propos, j'ai élargi cette réponse avec des informations sur une autre méthode (ou classe de méthodes) utilisant find, qui est très polyvalent.
Eliah Kagan
3
Merci pour la réponse détaillée findest en effet une bonne trouvaille.
1
+1 pour la réponse de bon sens (move-delete-move).
tu-Reinstate Monica-dor duh
Nit-pick: comment findgérer les noms de fichiers étranges avec *en eux mieux que la boucle for? La boucle for traite les fichiers avec *fin, parce que vous avez utilisé des guillemets doubles.
Flimm
28

Si vous utilisez bash(le shell par défaut), l' extgloboption shell vous permet d'utiliser une syntaxe de correspondance de motif étendue. Pour l'activer, utilisez la shoptcommande intégrée:

shopt -s extglob

(J'inclus cette ligne dans mon .bashrcdossier.)

Entre autres choses, il accorde un accès à l’ !()opérateur, qui correspond à n’importe quel modèle ne se trouvant pas à l’intérieur des parenthèses. Pour votre but:

rm !(filename).gif

Plus d'informations sont disponibles dans la section man bash"Correspondance de modèle".

Eswald
la source
1
Excellente réponse! Comme il est spécifique à Bash, il peut ne pas convenir à un script portable, mais à des tâches interactives et même à un script, c'est la meilleure solution. Il comporte la complexité supplémentaire qui extglobdoit être activée, mais rien ne se passe s'il est désactivé (dans ce cas, cela ne fonctionne tout simplement pas). Et il semble être activé par défaut, même si je n'ai shopt -s extglobdans aucun de mes fichiers de configuration (ou globaux). S'il existe une explication simple de la raison pour laquelle cela fonctionne pour moi immédiatement, vous pouvez l'ajouter à cette réponse; ou je peux poster une nouvelle question.
Eliah Kagan
13

Ouvrez un terminal avec Ctrl+ Alt+ Tet tapez:

find . -type f -name "*.gif" -and -not -name "filename.gif" -exec rm -vf {} \;

La différence entre -deleteet -exec rm -vf {} \;est qu'avec la deuxième option, vous pourrez voir quels fichiers ont été supprimés, à cause du -vdrapeau. C'est quelque chose qui ne peut pas être fait avec l' -deleteoption. ( -name -deleteimprimera les noms de fichiers, mais rmavec -vindique si chaque fichier a été supprimé avec succès .)

efthialex
la source
1

Il y a la GLOBIGNOREvariable. De man bash:

GLOBIGNORE
      A colon-separated list of patterns defining the set of filenames
      to be ignored by pathname expansion.  If a filename matched by a
      pathname expansion pattern also matches one of the  patterns  in
      GLOBIGNORE, it is removed from the list of matches.

Ainsi, un moyen simple consiste à définir GLOBGINOREle nom de fichier en question:

$ touch {a,b,c,d}.png
$ echo *.png
a.png b.png c.png d.png
$ GLOBIGNORE=c.png
$ echo *.png
a.png b.png d.png

Donc, dans votre cas:

GLOBIGNORE=filename.gif; rm *.gif

Bien sûr, puisque GLOBIGNOREcontient des modèles, vous pouvez l’utiliser chaque fois que vous souhaitez exclure un modèle:

$ GLOBIGNORE='[a-c].png'; echo *.png
d.png
$ touch bd.png; GLOBIGNORE='?.png'; echo *.png
bd.png

Un avantage (?!) GLOBIGNOREEst que ce paramètre exclut .et.. ne correspond pas automatiquement et permet dotglob:

The  GLOBIGNORE shell variable may be used to restrict the set of file‐
names matching a pattern.  If GLOBIGNORE is set, each matching filename
that also matches one of the patterns in GLOBIGNORE is removed from the
list of matches.  The filenames ``.''  and ``..''  are  always  ignored
when  GLOBIGNORE is set and not null.  However, setting GLOBIGNORE to a
non-null value has the effect of enabling the dotglob shell option,  so
all other filenames beginning with a ``.''  will match.  To get the old
behavior of ignoring filenames beginning with a ``.'', make ``.*''  one
of  the  patterns  in  GLOBIGNORE.  The dotglob option is disabled when
GLOBIGNORE is unset.

Donc, un moyen simple de supprimer tous les fichiers et dossiers d’un répertoire:

GLOBIGNORE=.; rm -r *

Les *noms de fichiers correspondant aux noms commençant par ., étant GLOBIGNOREactivés dotglob, mais ne correspondant pas .ou .., de sorte que vous n'affecterez pas le répertoire parent de cette façon.

muru
la source