web-dev-qa-db-fra.com

Comment supprimer tous les répertoires vides d'un sous-arbre?

Comment supprimer tous les répertoires vides d'un sous-arbre? 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.

159
maaartinus

Combinant GNU find options et prédicats, cette commande devrait faire le travail:

find . -type d -empty -delete
  • -type d restreint aux répertoires
  • -empty se limite aux vides
  • -delete supprime chaque répertoire

L'arbre est parcouru à partir des feuilles sans avoir besoin de spécifier -depth comme le laisse entendre -delete.

237

Liste 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 commande find dans son ensemble, pas seulement à rmdir. La redirection uniquement pour rmdir entraînerait un ralentissement important car vous auriez besoin d'invoquer un Shell intermédiaire.)

Vous pouvez éviter d'exécuter rmdir sur des répertoires non vides en passant le -empty prédicat à rechercher. GNU find teste le répertoire quand il est sur le point d'exécuter la commande, donc les répertoires qui viennent d'être vidés seront récupérés.

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

Une autre façon d'accélérer serait de regrouper les invocations rmdir. 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 dont vous disposez. Vous ne pouvez pas combiner -empty avec des méthodes de regroupement des appels, car les répertoires qui ne contiennent que des répertoires vides ne sont pas vides au moment où find les examine.

Une autre méthode consisterait à exécuter plusieurs passes. Que ce soit plus rapide dépend de beaucoup de choses, y compris si toute la hiérarchie des répertoires peut rester dans le cache disque entre les exécutions find.

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

Vous pouvez également utiliser zsh. Le qualificatif globF correspond aux répertoires non vides, donc /^F correspond à des répertoires vides. Les répertoires qui ne contiennent que des répertoires vides ne peuvent pas être mis en correspondance si facilement.

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

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

Si vous clouez juste un -p sur votre rmdir, cela fonctionnera en un seul passage. Ce ne sera ni joli ni 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 -empty test pour trouver, donc ça ne dérange pas avec les répertoires non vides.

6
mattdm

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

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

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

3
schily

J'utilise ces alias pour les commandes find fréquemment utilisées, en particulier lorsque je nettoie l'espace disque à l'aide de dupegur, où la suppression des doublons peut entraîner de nombreux répertoires vides.

Commentaires à l'intérieur .bashrc donc je ne les oublierai pas plus tard quand je devrai le modifier.

# 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 {} +'
0
raychi

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

Voici comment ça 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écutez rmdir sur la liste un par un
0
Ashish Ranjan