Unix - supprimer des fichiers et des dossiers à l'exclusion de PATTERN

10

Récemment, j'ai été confronté à la tâche de supprimer tous les fichiers / dossiers d'un répertoire, à l'exception de ceux correspondant à un modèle spécifique. J'ai donc préparé une commande unix sur une seule ligne pour faire le travail. Doit-il s'agir d'une seule ligne? Je suppose que non, mais c'est définitivement plus cool de cette façon!

Bien que le problème soit assez simple, j'ai été un peu surpris de la complexité de ma solution. Voici la commande que j'ai utilisée; REMARQUE: c'est une mauvaise solution car elle ne gère pas les noms de fichiers contenant des caractères de saut de ligne (ce qui n'avait pas d'importance dans ma situation).

ls | grep -v PATTERN | xargs -n1 -IREPLACE rm -rf REPLACE

Je n'ai pas utilisé la commande "find" car je ne veux pas rentrer dans les dossiers correspondant au MOTIF. Par exemple, considérez la structure de fichiers suivante:

file_foo.txt
first_dir
  |
  +--> contents
  +--> ...
foo_dir
  |
  +--> anotherfile.txt
  +--> morefiles.log
foo_file.txt
somefile.txt

L'utilisation du modèle "foo" ne doit supprimer que "first_dir" (et son contenu bien sûr) et "somefile.txt" ( pas "anotherfile.txt" ou "morefiles.log").

Question, existe-t-il de meilleures façons (plus élégantes et correctes) d'accomplir cela?


EDIT:
Il a été récemment porté à mon attention que "trouver" peut être une meilleure option:

find * -maxdepth 0 ! -name PATTERN -print0 | xargs -0n1 rm -rf

Cette solution gère correctement les chemins d'accès contenant des caractères de saut de ligne.

joxl
la source
EDIT: changé "the_dir" erroné pour corriger "first_dir".
joxl

Réponses:

10

Les exemples suivants ont été echopréfixés afin que vous puissiez tester les modèles avant de les utiliser réellement. Retirez le echopour activer le rm -rf. Remplacer rm -ripour demander une confirmation.

ksh a une extension de correspondance négative à son globbing:

# ksh
echo rm -rf !(*foo*)

La même syntaxe est disponible dans bash si vous définissez l' extgloboption:

# bash
shopt -s extglob
echo rm -rf !(*foo*)

zsh a sa propre syntaxe pour cela:

# zsh
setopt extended_glob
echo rm -rf ^*foo*

Il peut également utiliser la syntaxe de style ksh :

# zsh: ksh-style glob syntax
setopt ksh_glob no_bare_glob_qual
echo rm -rf !(*foo*)

# zsh: ksh-style glob syntax, adapted for the default bare_glob_qual option
setopt ksh_glob bare_glob_qual
echo rm -rf (!(*foo*))
Chris Johnsen
la source
Simple, élégant, gère les caractères de saut de ligne, (presque) une ligne (j'ai ajouté shopt -s extglobà mon fichier .bashrc, il s'agit donc maintenant d' une ligne). Parfait! Je promets un vote positif dès que ma réputation le permettra.
joxl
7

Voici une autre findsolution. Je ne suis pas sûr que cela ait un réel avantage sur le vôtre, mais cela n'a pas besoin xargset permet la rare possibilité que * s'étende à trop de noms.

find . -maxdepth 1 ! -name PATTERN -type f -delete

J'ai également ajouté -type fafin qu'il n'essaye pas de supprimer les répertoires.

Attention: -deleteest puissant. J'ai donné à l'un de mes fichiers de test 0 autorisations et la commande ci-dessus l'a supprimé sans hésitation.

garyjohn
la source
6
Testez d'abord. Remplacez -deletepar -printpour voir s'il trouve les fichiers que vous vouliez. Si tout va bien, utilisez l'historique des commandes (par exemple, appuyez sur le bouton haut) pour revenir à la commande précédente et vous assurer que vous travaillez avec les mêmes filtres de recherche.
Doug Harris
Lisez attentivement, notez que je ne veux supprimer les répertoires. Et find -deletene supprimera pas les répertoires non vides. Notez également que find -maxdepth 1correspond à "." (répertoire actuel) ce qui est vraiment mauvais. Bien que j'aime votre idée d'éliminer xargs, on pourrait utiliser find -execalternativement. find * -maxdepth 0 ! -name PATTERN -exec rm -rf '{}' \;
joxl
0

Vous pouvez utiliser cet exemple de script rebol http://askcodegeneration.com/keep-subversion/ à la place, qui est beaucoup plus compréhensible et qui peut gérer l'interaction utilisateur comme la sélection et la confirmation de dossier:

delete-file: func[file][
    if/else none? find file "/.svn/" [
        delete file
    ][
        print ["keeping" file]
    ]
]
dir: request-dir
ans: ask rejoin ["Do you confirm " dir "? (Yes): "]
if (ans = "Yes") [
    foreach-file dir :delete-file
]
Tutoriel Rebol
la source