TL; DR: la meilleure façon est d'utiliser à la -exec rm
place de -delete
.
find a \( -name b -prune \) -o -type f -exec rm {} +
Explication:
Pourquoi find se plaint-il lorsque vous essayez d'utiliser -delete
avec -prune
?
Réponse courte: car -delete
implique -depth
et -depth
rend -prune
inefficace.
Avant d'arriver à la réponse longue, observons d'abord le comportement de la recherche avec et sans -depth
:
$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2
Il n'y a aucune garantie sur la commande dans un seul répertoire. Mais il y a une garantie qu'un répertoire est traité avant son contenu. Notez foo/
avant tout foo/*
et foo/bar
avant tout foo/bar/*
.
Cela peut être inversé avec -depth
.
$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/
Notez que maintenant tous foo/*
apparaissent avant foo/
. Même chose avec foo/bar
.
Réponse plus longue:
-prune
empêche la recherche de descendre dans un répertoire. En d'autres termes, -prune
saute le contenu du répertoire. Dans votre cas, -name b -prune
empêche la recherche de descendre dans n'importe quel répertoire portant le nom b
.
-depth
fait rechercher pour traiter le contenu d'un répertoire avant le répertoire lui-même. Cela signifie qu'au moment où find parvient à traiter l'entrée de répertoire, b
son contenu a déjà été traité. Ainsi -prune
est inefficace avec -depth
en vigueur.
-delete
implique -depth
qu'il peut d'abord supprimer les fichiers, puis le répertoire vide. -delete
refuse de supprimer les répertoires non vides. Je suppose qu'il serait possible d'ajouter une option pour forcer -delete
à supprimer les répertoires non vides et / ou empêcher -delete
d'impliquer -depth
. Mais c'est une autre histoire.
Il existe une autre façon de réaliser ce que vous voulez:
find a -not -path "*/b*" -type f -delete
Cela peut ou non être plus facile à retenir.
Cette commande descend toujours dans le répertoire b
et traite chaque fichier qu'il contient uniquement pour -not
les rejeter. Cela peut être un problème de performances si le répertoire b
est énorme.
-path
fonctionne différemment de -name
. -name
ne correspond qu'au nom (du fichier ou du répertoire) tandis qu'il -path
correspond au chemin entier. Par exemple, observez le chemin /home/lesmana/foo/bar
. -name -bar
correspondra car le nom est bar
. -path "*/foo*"
correspondra car la chaîne se /foo
trouve dans le chemin. -path
a quelques subtilités que vous devez comprendre avant de l'utiliser. Lisez la page de manuel de find
pour plus de détails.
Attention, ce n'est pas infaillible à 100%. Il y a des chances de "faux positifs". La façon dont la commande est écrite ci-dessus ignorera tout fichier contenant un répertoire parent dont le nom commence par b
(positif). Mais il ignorera également tout fichier dont le nom commence par, b
quelle que soit sa position dans l'arbre (faux positif). Cela peut être corrigé en écrivant une meilleure expression que "*/b*"
. Cela reste un exercice pour le lecteur.
Je suppose que vous avez utilisé a
et b
comme espaces réservés et les vrais noms ressemblent plus à allosaurus
et brachiosaurus
. Si vous mettez brachiosaurus
en place, b
la quantité de faux positifs sera considérablement réduite.
Au moins, les faux positifs ne seront pas supprimés, ce ne sera donc pas aussi tragique. De plus, vous pouvez vérifier les faux positifs en exécutant d'abord la commande sans -delete
(mais n'oubliez pas de placer l'implicite -depth
) et examinez la sortie.
find a -not -path "*/b*" -type f -depth
a
saufa/b
?