Python поиск папок без файлов на диске
Как найти все пустые папки на диске, желательно не рекурсивно а итеративно.
Есть рекурсивная функция поиска папок, но во-первых, она рекурсивная а не итеративная, а во-вторых, она не находит пустые вложенные папки, я имею ввиду "/home/1/2/3/4/5" она покажет, только, что папка 5 пустая, но то что, перед ней все папки тоже пустые (без файлов) она не показывает.
empty_dir = []
def find_empty_dirs(path):
global empty_dir
try:
for d in listdir(path):
a = join(path, d)
if isdir(a):
find_empty_dirs(a)
try:
if not listdir(a): empty_dir.append(normpath(a))
except PermissionError:
pass
except PermissionError:
pass
Ответы (4 шт):
Ну так нужно возвращать наверх признак, что папки внутри пустые. И проверять, что все обойденные папки пустые для этого. Примерно так (код не проверял, но суть должна быть такая):
def find_empty_dirs(path, empty_dir):
try:
is_empty = True
for d in listdir(path):
a = join(path, d)
if isdir(a):
if not find_empty_dirs(a, empty_dir):
is_empty = False
else:
is_empty = False
empty_dir.append(normpath(path))
return is_empty
except PermissionError:
return False
empty_dir = []
path = '/'
find_empty_dirs(path, empty_dir)
В-нулевых, никогда не используйте конструкции вида from os import *, from os.path import *! Никогда! Вы засоряете пространство глобальных символов множеством имён которых не знаете и которые рано или поздно пересекутся с именами ваших переменных и функций. Совершенно безобидный код вдруг поменяет своё поведение и вам будут гарантированы часы отладки со словами "я же тут написал как надо, почему оно не вызывается?"
Во-первых, никаких собираний результатов в список. Обход каталогов длительная операция, выдавать данные наверх нужно как только они стали доступны. Так что нужно делать генератор, который будет выдавать наверх каталог и флажок (не)пустой. Генератор пока будет рекурсивный - так проще. Если при обработке каталога встретился не каталог, то текущий каталог будет отправлен как не пустой. То же самое будет если нашёлся не пустой подкаталог.
Во-вторых, обработку ошибок надо локализовать. Если закрывать блоками try большие куски кода вы потеряете часть информации. Например, при обработке каталога в середине проверка подкаталога ошиблась. Тогда весь хвост каталога не будет обработан.
Рекурсивная программа:
import os
import sys
def listdir(path):
try:
yield from os.listdir(path)
except PermissionError:
print(f'find-empty-dirs: ‘{path}’: Permission denied', file=sys.stderr)
def isdir(path):
try:
return os.path.isdir(path)
except PermissionError:
print(f'find-empty-dirs: ‘{path}’: Permission denied', file=sys.stderr)
return False
def dirs(path):
empty = True
for name in listdir(path):
entry = os.path.join(path, name)
if isdir(entry):
for e, p in dirs(entry):
if e:
yield e, p
else:
empty = False
else:
empty = False
yield empty, path
def empty_dirs(path):
for e, p in dirs(path):
if e:
yield p
def main():
for path in sys.argv[1:]:
for d in empty_dirs(path):
print(d)
main()
Нерекурсивная версия рекурсивного генератор потребует явного стека, который хранит тройки [путь, (не)пусто, итератор]. Итератор двигается по каталогу с помощью вызова next(итератор, None). Признаки не пустоты обновляются при движении по каталогам и передаются наверх когда каталог кончился. Из рекурсивной программы нужно убрать функции dirs, empty_dirs и добавить эту:
def empty_dirs(root):
stack = [[root, True, listdir(root)]]
while stack:
path, empty, it = stack[-1]
name = next(it, None)
if name is None:
stack.pop()
if empty:
yield path
else:
if stack:
stack[-1][1] = False
else:
entry = os.path.join(path, name)
if isdir(entry):
stack.append([entry, True, listdir(entry)])
else:
stack[-1][1] = False
Старый вопрос, ну уж ответы больно странные, тормозные и запутанные.
import os
def empty_dirs(path):
for dirpath, _, filenames in os.walk(path):
if not filenames:
yield dirpath
P.S.
Если толковать "...папки тоже пустые (без файлов)..." расширено, как "...папки и подпапки тоже пустые (без файлов)...", то нужно добавить ещё одно условие:
import pathlib # TODO: убрать, не тормозить и использовать str.startswith()
def empty_trees(path):
not_empty_path = pathlib.PurePath("")
for dirpath, _, filenames in os.walk(path, topdown=False):
if filenames:
not_empty_path = pathlib.PurePath(dirpath)
elif not not_empty_path.is_relative_to(dirpath):
yield dirpath
import os
from collections import deque
# начальная директория для обхода
v_root = r"D:\\"
# список для хранения путей к папкам без файлов
lst_empty_dirs = []
# очередь для итеративного обхода (ширина)
lst_queue = deque([v_root])
# основной цикл обхода
while lst_queue:
v_current = lst_queue.popleft()
try:
# сканируем содержимое текущей папки
with os.scandir(v_current) as i:
lst_entries = list(i)
# собираем только файлы
lst_files = [v_entry for v_entry in lst_entries if not v_entry.is_dir()]
# если файлов нет — считаем папку "пустой"
if not lst_files:
lst_empty_dirs.append(v_current)
# добавляем все подпапки в очередь для дальнейшего обхода
lst_subdirs = [v_entry.path for v_entry in lst_entries if v_entry.is_dir()]
lst_queue.extend(lst_subdirs)
except PermissionError:
# если нет доступа к папке — пропускаем
pass
# выводим все найденные пустые папки
print(f'Список каталогов, не содержащих файлы, на диске '
'{v_root.replace("\\", "").replace("//", "").replace(":", "")}:')
for v_path in lst_empty_dirs:
print(v_path)
input("Нажмите Enter для продолжения...")