Вопрос или проблема
Следующий скрипт не рекурсивно удаляет обычные пустые файлы, не включая точечные, а затем не рекурсивно удаляет пустые каталоги, не включая точечные, и игнорирует наличие файлов .DS_Store
(то есть, если в каталоге содержится только файл .DS_Store
, этот каталог всё равно будет удалён).
rm -f -- *(.L0)
setopt extended_glob
has_files_other_than_DS_Store() [[ -n $REPLY/^(#i).ds_store(#qNDY1) ]]
remove_empty_macos_dirs() {
local dir ret=0
for dir do
rm -f -- $dir/.(#i)ds_store(N.) && rmdir -- $dir || ret=$?
done
return $ret
}
remove_empty_macos_dirs *(/^+has_files_other_than_DS_Store)
Может, кто-нибудь покажет, как сделать это рекурсивно? То есть первый шаг — рекурсивно удалить все пустые обычные файлы. А затем рекурсивно удалить пустые каталоги.
Я бы попытался сделать это сам, но, если честно, я просто боюсь экспериментировать с командой rm
.
Чтобы сделать это рекурсивно, нужно выполнять это в глубину, так что, если, например, dirA
содержит только dirB
, сам по себе пустой, вы сначала удаляете dirB
, чтобы dirA
затем стал пустым и мог быть удалён по очереди.
Глобусы zsh
имеют квалификатор od
для d
eepth-first o
rdering:
set -o extendedglob
zmodload zsh/files # получить встроенные rm/rmdir для улучшенной производительности
# и также избежать ограничений E2BIG их автономных
# эквивалентов
rmall() {
# раздражающе встроенный rm в zsh жалуется, если не дано никаких аргументов
# даже с -f
(( $# == 0 )) || rm -f -- "$@"
}
ret=0
rmall **/*(ND.L0) # сначала удалите пустые обычные файлы
for dir (**/*(ND/od)) ()(($#)) $dir/^(#i).ds_store(NDY1) || {
rmall $dir/.(#i)ds_store(N.) && rmdir -- $dir || ret=$?
}
Вы можете заменить rm
/rmdir
на print -r Would rm
/print -r Would rmdir
, чтобы сначала протестировать, но это не обязательно даст тот же эффект, так как print
не удалит никаких файлов и не сможет сделать каталоги пустыми.
Ответ или решение
Для выполнения задачи рекурсивного удаления пустых файлов и директорий в Unix-подобных системах с использованием оболочки Zsh предлагается следующий подробный подход. Это поможет вам эффективно и безопасно произвести необходимые действия, минимизируя риск потери данных.
Шаг 1: Удаление пустых файлов
Первоначально нам следует удалить пустые регулярные файлы. Мы воспользуемся встроенной командой rm
в Zsh, которая обеспечивает более высокую производительность. Для этого применим следующий код:
setopt extendedglob
zmodload zsh/files # Загружаем модуль для работы с файлами
rm_all() {
# Проверка на наличие аргументов для rm
(( $# == 0 )) || rm -f -- "$@"
}
# Удаление всех пустых регулярных файлов рекурсивно
rm_all **/*(ND.L0)
Здесь **/*(ND.L0)
используется для нахождения всех пустых файлов, где:
**
— рекурсивный поиск,*
— все файлы и директории,(ND.L0)
— фильтр для выбора пустых файлов.
Шаг 2: Удаление пустых директорий
После того как все пустые файлы удалены, мы можем перейти к удалению пустых директорий. В этом этапе нам необходимо убедиться, что мы также игнорируем скрытые файлы .DS_Store
и сохраняем правильный порядок удаления, чтобы сначала удалять вложенные, а затем родительские директории. Код для этой операции выглядит следующим образом:
# Функция для удаления пустых директорий
remove_empty_dirs() {
local dir ret=0
for dir (**/*(ND/od)) ; do
# Проверяем наличие файлов, отличных от .DS_Store
if [[ -z $dir/^(#i).ds_store(NDY1) ]]; then
# Пытаемся удалить .DS_Store и затем директорию
rm_all $dir/.(#i)ds_store(N.) && rmdir -- $dir || ret=$?
fi
done
return $ret
}
# Вызов функции для удаления пустых директорий
remove_empty_dirs
В этой функции:
(**/*(ND/od))
позволяет нам находить директории в порядке глубины, чтобы удалять сначала вложенные директории.- Условия
[[ -z $dir/^(#i).ds_store(NDY1) ]]
проверяют, что директория не содержит других файлов, кроме скрытых.DS_Store
. Если таковые есть, директория будет удалена.
Предварительное тестирование
Прежде чем запускать команды rm
и rmdir
, рекомендуется протестировать скрипт, чтобы избежать случайного удаления данных. Для этого команды можно заменить на print -r Would rm
и print -r Would rmdir
:
# Тестирование перед удалением
_test_rmall() {
# Аналогично функции rm_all, только для тестирования
(( $# == 0 )) || print -r Would rm -- "$@"
}
# Замените rm_all на _test_rmall и проведите тестирование
_test_rmall **/*(ND.L0)
Заключение
Следуя описанным шагам, вы сможете безопасно и эффективно удалить пустые файлы и директории в вашей файловой системе. Пользуясь данным подходом, вы уменьшите риск случайной потери данных и обеспечите правильную последовательность операций. Обязательно тщательно протестируйте свои команды перед их использованием на реальных данных, чтобы гарантировать безопасность и безошибочность выполнения операций.