Jak mohu odstranit všechny prázdné adresáře v podstromu? Použil jsem něco jako
find . -type d -exec rmdir {} 2>/dev/null \;
ale musím běžet vícekrát, abych odstranil pouze adresáře obsahující prázdné adresáře. Navíc je to docela pomalé, zejména u cygwina.
Při kombinaci GNU find
možností a predikátů by tento příkaz měl provést tuto úlohu:
find . -type d -empty -delete
-type d
omezuje na adresáře-empty
omezuje se na prázdné-delete
odebere každý adresářStrom je vycházel z listů bez nutnosti specifikovat -depth
jak to implikuje -delete
.
Seznamy adresářů hluboce vnořených-první.
find . -depth -type d -exec rmdir {} \; 2>/dev/null
(Všimněte si, že přesměrování se vztahuje na příkaz find
jako celek, nejen na příkaz rmdir
. Přesměrování pouze pro rmdir
by způsobilo výrazné zpomalení, protože byste museli vyvolat prostřední Shell.)
Můžete se vyhnout spuštění rmdir
na neprázdných adresářích předáním -empty
predikát k nalezení. GNU find testuje adresář, když se chystá spustit příkaz, takže budou odebrány právě vyprázdněné adresáře).
find . -depth -type d -empty -exec rmdir {} \;
Dalším způsobem, jak urychlit, by bylo seskupení rmdir
vyvolání. Oba pravděpodobně budou znatelně rychlejší než původní, zejména za Cygwina. Neočekávám velký rozdíl mezi těmito dvěma.
find . -depth -type d -print0 | xargs -0 rmdir 2>/dev/null
find . -depth -type d -exec rmdir {} + 2>/dev/null
Která metoda je rychlejší záleží na tom, kolik neprázdných adresářů máte. Nemůžete kombinovat -empty
s metodami pro seskupování vyvolání, protože pak adresáře, které obsahují pouze prázdné adresáře, nejsou do doby find
prázdné.
Další metodou by bylo spuštění více průchodů. Zda je to rychlejší, záleží na mnoha věcech, včetně toho, zda celá hierarchie adresářů může zůstat v mezipaměti disku mezi spuštěním find
.
while [ -n "$(find . -depth -type d -empty -print -exec rmdir {} +)" ]; do :; done
Případně použijte zsh. kvalifikátor globF
odpovídá neprázdným adresářům, takže /^F
odpovídá prázdným adresářům. Adresáře, které obsahují pouze prázdné adresáře, nelze tak snadno sladit.
while rmdir **/*(/N^F); do :; done
(Toto skončí, když rmdir
obdrží prázdný příkazový řádek.)
Pokud si jen připínáš -p
na vašem rmdir
, bude to fungovat v jednom průchodu. Nebude to hezké nebo optimální, ale mělo by to všechno. To rmdir říká, aby odstranil všechny neprázdné nadřazené adresáře toho, který odstraňujete.
Trochu můžete ušetřit přidáním -empty
test najít, takže se neobtěžuje s neprázdné adresáře.
find . -depth -type d -exec rmdir {} +
je nejjednodušší a standardní odpověď na tuto otázku.
Ostatní zde uvedené odpovědi bohužel závisí na vylepšeních specifických pro dodavatele, která ve všech systémech neexistují.
Tyto aliasy používám pro často používané příkazy find
, zejména když vyčistím místo na disku pomocí dupegur, kde odstranění duplikátů může vést k mnoha prázdným adresářům.
Komentáře uvnitř .bashrc
tak na ně nezapomenu později, když to potřebuji vyladit.
# 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 {} +'
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
Funguje to takto:
rmdir
v seznamu jeden po druhém