Як видалити всі порожні каталоги в піддерев’ї?


151

Як я можу видалити всі порожні каталоги в піддерев'ї? Я використовував щось подібне

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

але мені потрібно запустити кілька разів, щоб видалити каталоги, що містять лише порожні каталоги. Більше того, це досить повільно, особливо під цигуніном.


Дивіться також emacs.stackexchange.com/q/12190/2264 для рішення emacs.
Шон Аллред

Відповіді:


221

Поєднуючи findпараметри та предикати GNU , ця команда повинна виконати цю роботу:

find . -type d -empty -delete
  • -type d обмежується каталогами
  • -empty обмежується порожніми
  • -delete видаляє кожен каталог

Дерево ходить з листя без необхідності вказувати, -depthяк це мається на увазі -delete.


2
-deleteвже означає, -depthтому вам не потрібно вказувати це вручну.
jamadagni

1
Спасибі, я цього не усвідомлював. Відповідь оновлено.
Крістоф Древет-Дрогует,

11
Я додам -mindepth 1тут, щоб не видаляти сам стартовий каталог, якщо він буде порожнім.
Грег Дубіцький


2
!має особливе значення для оболонки. Вам потрібно уникнути цього. Щось на кшталт: \! -name 'Completed'безпосередньо перед цим -deleteслід попрацювати. Або ви просто помістите файл маркера в цей каталог.
Крістоф Древет-Дрогует

53

Перерахуйте каталоги глибоко вкладені першими.

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

(Зверніть увагу, що перенаправлення застосовується до findкоманди в цілому, а не лише до rmdir. Перенаправлення лише на назовні rmdirможе спричинити значне уповільнення, оскільки вам потрібно буде викликати проміжну оболонку.)

Ви можете уникнути запуску rmdirв непорожніх каталогах, передавши -emptyприсудок на пошук. GNU знаходить тести каталогів, коли збирається запустити команду, тому щойно випорожнені каталоги будуть вибрані.

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

Ще один спосіб прискорити роботу - це групувати rmdirвиклики. Обидва, ймовірно, будуть помітно швидшими за оригінал, особливо під Cygwin. Я не очікую великої різниці між цими двома.

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

Який спосіб швидший, залежить від того, скільки у вас є порожніх каталогів. Ви не можете поєднувати -emptyз методами групування викликів, оскільки тоді каталоги, які містять лише порожні каталоги, не порожні до того часу, findяк їх переглядають.

Іншим методом було б запуск декількох проходів. Чи буде це швидше, залежить від багатьох речей, включаючи, чи може вся ієрархія каталогів залишатися в кеш-диску між findзапусками.

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

Як варіант, використовуйте zsh. Глоба відбіркові F матчі непустих каталогів, тому /^Fвідповідає порожнім каталогам. Каталоги, які містять лише порожні каталоги, не можуть бути зібрані так легко.

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

(Це закінчується, коли rmdirнадходить порожній командний рядок.)


Це воно. Замість 90 секунд потрібно 0,90 с.
maaartinus

@maaartinus: Мені цікаво: чи є у вас подібний набір даних, де можна було б спробувати -p? Я б не думав, що це матиме значення.
Жиль

3
@maartinus - інші невеликі оптимізації: додавання -emptyмає працювати з цим (хоча я не впевнений, наскільки точно це отримає). І дуже, дуже банально, оскільки ви, мабуть, не хочете видаляти ., користуйтеся -mindepth 1.
mattdm

Майже весь час це було не видаленням, а запуском процесу. Я не помітив -depthаргументу, який робить rmdir -pмарним. Я вже змінив свій коментар. 90-ті роки були моєю оригінальною спробою; тут немає нічого дивного.
maaartinus

2
Я зрозумів, що ми можемо rmdirвзагалі зняти командний виклик, принаймні, з GNU find, за допомогою цієї команди:find . -depth -type d -empty -delete
Крістоф Древет-Дрогует

6

Якщо ви просто вставте -pсвій rmdir, це спрацює за один прохід. Це не буде красивим чи оптимальним, але все має отримати. Це говорить rmdir про видалення будь-яких непорожніх батьківських каталогів того, який ви видаляєте.

Ви можете трохи заощадити, додавши -emptyтест, щоб знайти, так що це не турбує непорожні каталоги.


3

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

- це найпростіша і стандартна відповідь на це питання.

Інші відповіді, наведені тут, на жаль, залежать від конкретних удосконалень для постачальника, які існують не у всіх системах.


3
Ця відповідь викликає помилку для кожного каталогу, яку неможливо видалити, що може бути менше бажаного.
Віллем ван Кетвіч

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

Ось як це працює:

  1. Рекурсивно перераховуйте всі каталоги разом з їх глибиною
  2. Сортувати за низхідним порядком їх глибини
  3. Відфільтруйте лише контури каталогів
  4. Виконайте rmdirза списком по черзі

0

Я використовую ці псевдоніми для часто використовуваних findкоманд, особливо коли я прибираю простір на диску за допомогою dupeguru , де видалення дублікатів може призвести до безлічі порожніх каталогів.

Коментарі всередині, .bashrcтож я не забуду їх пізніше, коли мені потрібно буде його переробити.

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

-2

rm -r */команда працювала для мене легко. rmповинно вимагати -fпримусового видалення каталогів з файлами. rm -rслід видаляти лише порожні каталоги. Я відкритий для того, чому це може бути неправильним. Це також має залишити файли, оскільки */дивиться лише на папки.


1
Я настійно рекомендую спочатку все ретельно перевірити, оскільки rmв першу чергу мається на увазі видалення файлів. Хоча */тільки відповідає каталогам, я не маю уявлення, що це робить на більш глибоких рівнях. Я також можу уявити, що він працює лише в деяких системах.
maaartinus
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.