Рекурсивно удалите пустые файлы, а затем пустые директории.

Вопрос или проблема

Следующий скрипт не рекурсивно удаляет обычные пустые файлы, не включая точечные, а затем не рекурсивно удаляет пустые каталоги, не включая точечные, и игнорирует наличие файлов .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 для deepth-first ordering:

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)

Заключение

Следуя описанным шагам, вы сможете безопасно и эффективно удалить пустые файлы и директории в вашей файловой системе. Пользуясь данным подходом, вы уменьшите риск случайной потери данных и обеспечите правильную последовательность операций. Обязательно тщательно протестируйте свои команды перед их использованием на реальных данных, чтобы гарантировать безопасность и безошибочность выполнения операций.

Оцените материал
Добавить комментарий

Капча загружается...