Удаление пустых деревьев каталогов (удаление как можно большего количества каталогов, но без файлов)

12

Предположим, у меня есть дерево каталогов, подобное этому:

ROOTDIR
    └--SUBDIR1
        └----SUBDIR2
            └----SUBDIR3

Я ищу команду, которая при вводе:

$ [unknown command] ROOTDIR

Все дерево каталогов может быть удалено, если в нем нет файла, а есть только каталоги . Однако, скажем, если есть файл с именем hello.pdf в SUBDIR1:

ROOTDIR
    └--SUBDIR1
        └--hello.pdf
        └----SUBDIR2
            └----SUBDIR3

Тогда команда должна удалить только SUBDIR2 и ниже.

gsklee
источник
Кажется, это дубликат Как удалить все пустые каталоги в поддереве?
Дэвид Кэри

Ответы:

11

Алексис близко Что вам нужно сделать, это:

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

Это сначала развернет дерево каталогов, пока не найдет первый пустой каталог, а затем удалит его. Таким образом, создание родительского каталога пустым, который затем будет удален и т. Д. Это даст желаемый эффект (я делаю это, вероятно, 10 раз в неделю, поэтому я почти уверен, что это правильно). :-)

Стив Юранич
источник
Зачем -depthнужен вариант? find . -type d -empty -exec rmdir "{}" \;тоже должно работать .... верно?
Абхишек А
4
Подумайте, есть ли у вас дерево (только для каталогов) foo/bar/baz. Если вы не используете -depth, он будет пытаться удалить fooсначала, потерпеть неудачу, и вы получите в результате foo/barпосле запуска.
10
1
Возможно, альтернативой является использование +вместо того, ;чтобы вы пакетно удаляли каталоги. Поскольку вы делаете это в глубину, дети все равно будут удалены раньше родителей (возможно, это зависит от вашей версии rmdir / bash и зависит от того, не удалит ли rmdir непустые каталоги). Это работает для меня в Bash на mkdir -p a/b/c/d ; find a -depth -type d -exec rmdir {} +
Cygwin
3
Люди, пойдите для гораздо более краткого ответа go2null ниже! Не могу понять, почему SE отдает предпочтение принятым ответам, а не ответам с наибольшим количеством голосов при отображении ответов ниже вопроса. ФП принимает лучший ответ, доступный в момент выбора, но позже могут прийти гораздо лучшие ответы, которые сообщество поддерживает, не так ли? (Конечно, это что-то для мета ...)
Джамадагни
это не работает для меня. он удаляет только самый глубокий лист (в данном случае SUBDIR3)
Джои Барух,
23
find ROOTDIR -type d -empty -delete

такой же как

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

но использует встроенное действие "-delete".

Обратите внимание, что «-delete» подразумевает «-depth».

go2null
источник
Престижность для наиболее краткого ответа с помощью встроенного удаления find! Я добавляю это в мои локальные утилиты!
Джамадагни
3

Я бы попробовал это:

find ROOTDIR -type d -depth -exec rmdir {} \;
Alexis
источник
1

Вот некоторые требования, прежде чем мы сможем сделать это безопасно:

  1. сначала удалите подкаталоги, а затем каталоги верхнего уровня, т.е. нам нужно отсортировать список каталогов или использовать флаг rmdir --parents
  2. всегда запускайте ROOTDIR с / или ./, чтобы избежать неожиданностей с файлами, начинающимися с -
  3. использовать NUL завершенный список каталогов для работы с именами каталогов с пробелами

Вот как я это сделаю в оболочке:

find ./ROOTDIR -type d | sort -r | tr '\n' '\000' | xargs -0 rmdir --ignore-fail-on-non-empty

Если вы не возражаете против некоторых избыточных ошибок, вы можете просто принудительно удалить все каталоги с родителями, и вам не нужно выполнять какую-либо сортировку (вы не можете сортировать завершенные строки NUL, что добавляет необходимость в tr)

find ./ROOTDIR -type d -print0 | xargs -0 rmdir --ignore-fail-on-non-empty --parents
пума
источник
Слава за подробное объяснение вашего ответа. Вероятно, я бы использовал тот же подход, пока не узнал об -empty -deleteопциях findв ответе @ go2null.
Давор Кубранич
0
rmdir $(find ROOTDIR -type d | sort -r)
lanzz
источник
5
Это не сработает, если любое из имен каталогов содержит пробелы или символы-заглушки. Обычно плохая идея использовать подстановку команд в списке имен файлов. Это особенно плохая идея с , findпотому что findесть способ сделать обработку чисто: find … -exec.
Жиль "ТАК - перестать быть злым"
Спасибо Жилю за то, что указал на это. @lanzz, обычно просто отправка команды без объяснения того, что она делает (и в этом случае ловушки) недостаточно. Пожалуйста, добавьте к своему ответу.
n0pe
0

Я бы сделал это:

find ROOTDIR -type d | xargs -0 -I {} rmdir {}
chemila
источник