Comment supprimer tous les répertoires vides d'une sous-arborescence?

151

Comment puis-je supprimer tous les répertoires vides d'une sous-arborescence? J'ai utilisé quelque chose comme

find . -type d -exec rmdir {} 2>/dev/null \;

mais je dois être exécuté plusieurs fois afin de supprimer les répertoires contenant uniquement des répertoires vides. De plus, c'est assez lent, surtout sous cygwin.

maaartinus
la source
Voir aussi emacs.stackexchange.com/q/12190/2264 pour une solution emacs.
Sean Allred

Réponses:

222

En combinant les findoptions GNU et les prédicats, cette commande devrait faire le travail:

find . -type d -empty -delete
  • -type d restreint aux répertoires
  • -empty restreint à vider
  • -delete supprime chaque répertoire

L’arbre est parcouru des feuilles sans qu’il soit nécessaire de spécifier -depthce qu’il sous-entend -delete.

Christophe Drevet-Droguet
la source
2
-deleteimplique déjà -depth, vous n'avez donc pas besoin de spécifier cela manuellement.
jamadagni
1
Merci, je ne m'en suis pas rendu compte. Réponse mise à jour.
Christophe Drevet-Droguet le
11
J'ajouterais -mindepth 1ici, pour empêcher la suppression du répertoire de départ lui-même, s'il était vide.
Greg Dubicki
2
Génial, mais ne fonctionne pas sur mes anciens hôtes SunOS ...
dimanche
2
!a une signification spéciale pour la coquille. Vous devez y échapper. Quelque chose comme: \! -name 'Completed'juste avant -deletedevrait fonctionner. Ou vous venez de mettre un fichier marqueur dans ce répertoire.
Christophe Drevet-Droguet
53

Listez les répertoires profondément imbriqués en premier.

find . -depth -type d -exec rmdir {} \; 2>/dev/null

(Notez que la redirection s’applique à la findcommande dans son ensemble, pas seulement rmdir. Redirection uniquement pour rmdirprovoquerait un ralentissement important, car vous auriez besoin d’appeler un shell intermédiaire.)

Vous pouvez éviter de s'exécuter rmdirsur des répertoires non vides en transmettant le -emptyprédicat à rechercher. GNU find teste le répertoire lorsqu'il est sur le point d'exécuter la commande. Ainsi, les répertoires qui viennent d'être vides seront récupérés.

find . -depth -type d -empty -exec rmdir {} \;

Une autre façon d'accélérer serait de regrouper les rmdirinvocations. Les deux sont susceptibles d'être sensiblement plus rapides que l'original, en particulier sous Cygwin. Je ne m'attends pas à beaucoup de différence entre ces deux.

find . -depth -type d -print0 | xargs -0 rmdir 2>/dev/null
find . -depth -type d -exec rmdir {} + 2>/dev/null

La méthode la plus rapide dépend du nombre de répertoires non vides que vous avez. Vous ne pouvez pas combiner -emptydes méthodes de regroupement d'appels, car les répertoires qui ne contiennent que des répertoires vides ne sont pas vides pour le moment find.

Une autre méthode consiste à exécuter plusieurs passes. Cela dépend de nombreux facteurs, notamment de la possibilité que toute la hiérarchie des répertoires reste dans le cache du disque entre les findexécutions.

while [ -n "$(find . -depth -type d -empty -print -exec rmdir {} +)" ]; do :; done

Sinon, utilisez zsh. Le qualificatif glob F correspond aux répertoires non vides, donc aux /^Frépertoires vides. Les répertoires qui ne contiennent que des répertoires vides ne peuvent pas être appariés aussi facilement.

while rmdir **/*(/N^F); do :; done

(Cela se termine lorsque rmdirreçoit une ligne de commande vide.)

Gilles
la source
C'est ça. Au lieu de 90 secondes, cela prend 0,90 s.
Maaartinus
@ Maaartinus: Je suis curieux: avez-vous un ensemble de données similaire où vous pourriez essayer sans -p? Je n'aurais pas pensé que cela ferait une différence.
Gilles
3
@ maartinus - autres petites optimisations: l'ajout -emptydevrait fonctionner avec celui-ci (bien que je ne sois pas sûr de combien il gagnera). Et très, très trivialement, puisque vous ne voulez probablement pas enlever ., utilisez -mindepth 1.
Mattdm
Ce n’était pas la suppression, mais le démarrage du processus, ce qui prenait presque tout le temps. J'avais négligé l' -depthargument, ce qui rend rmdir -pinutile. J'ai déjà changé mon commentaire. Le 90 s était ma tentative initiale; il n'y a rien d'étonnant ici.
Maaartinus
2
J'ai réalisé que nous pouvions supprimer rmdircomplètement l'appel de commande, du moins avec GNU find, avec cette commande:find . -depth -type d -empty -delete
Christophe Drevet-Droguet le
6

Si vous virez juste -psur votre rmdir, qui va travailler en un seul passage. Ce ne sera pas beau ou optimal, mais il devrait tout avoir. Cela indique à rmdir de supprimer tous les répertoires parents non vides de celui que vous supprimez.

Vous pouvez économiser un peu en ajoutant le -emptytest à find, de sorte qu'il ne s'embarrasse pas de répertoires non vides.

mattdm
la source
3

find . -depth -type d -exec rmdir {} +

est la réponse la plus simple et conforme à la norme à cette question.

Les autres réponses données ici dépendent malheureusement toutes d’améliorations spécifiques aux fournisseurs qui n’existent pas sur tous les systèmes.

schily
la source
3
Cette réponse soulève une erreur pour chaque répertoire qui ne peut pas être supprimé et qui peut ne pas être souhaitable.
Willem van Ketwich
0

find . -type d -printf "%d %p\n" |\ sort -nr |\ perl -pe 's/^\d+\s//;' |\ while read dir; do \ (rmdir "$dir" > /dev/null 2>&1); \ done

Voilà comment cela fonctionne:

  1. Liste récursivement tous les répertoires avec leur profondeur
  2. Trier par ordre décroissant de leur profondeur
  3. Filtrer uniquement les chemins de répertoire
  4. Exécuter rmdirsur la liste un par un
Ashish Ranjan
la source
0

J'utilise ces alias pour les findcommandes fréquemment utilisées , notamment lorsque je nettoie de l'espace disque à l'aide de dupeguru , où la suppression des doublons peut générer de nombreux répertoires vides.

Commentaires à l'intérieur .bashrcafin que je ne les oublie pas plus tard quand je dois le peaufiner.

# find empty directories
alias find-empty='find . -type d -empty'

# fine empty/zero sized files
alias find-zero='find . -type f -empty'

# delete all empty directories!
alias find-empty-delete='find-empty -delete'

# delete empty directories when `-delete` option is not available.
# output null character (instead of newline) as separator. used together
# with `xargs -0`, will handle filenames with spaces and special chars.
alias find-empty-delete2='find-empty -print0 | xargs -0 rmdir -p'

# alternative version using `-exec` with `+`, similar to xargs.
# {}: path of current file
# +: {} is replaced with as many pathnames as possible for each invocation.
alias find-empty-delete3='find-empty -exec rmdir -p {} +'

# for removing zero sized files, we can't de-dupe them automatically
# since they are technically all the same, so they are typically left
# beind. this removes them if needed.
alias  find-zero-delete='find-zero -delete'
alias find-zero-delete2='find-zero -print0 | xargs -0 rm'
alias find-zero-delete3='find-zero -exec rm {} +'
Raychi
la source
-2

rm -r */la commande a fonctionné facilement pour moi. rmdevrait obliger -fà supprimer de force les répertoires avec les fichiers. rm -rne devrait supprimer que les répertoires vides. Je suis ouvert aux raisons pour lesquelles cela pourrait être faux. Cela devrait également laisser des fichiers puisque */regarde seulement les dossiers.

libroman2
la source
1
Je vous recommande fortement de le tester en premier car il rmest principalement destiné à supprimer des fichiers. Tandis que */seulement correspond aux répertoires, je n'ai aucune idée de ce que cela fait à des niveaux plus profonds. Je peux aussi imaginer que cela ne fonctionne que sur certains systèmes.
Maaartinus