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 шт):

Автор решения: CrazyElf

Ну так нужно возвращать наверх признак, что папки внутри пустые. И проверять, что все обойденные папки пустые для этого. Примерно так (код не проверял, но суть должна быть такая):

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)
→ Ссылка
Автор решения: Stanislav Volodarskiy

В-нулевых, никогда не используйте конструкции вида 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
→ Ссылка
Автор решения: Serge3leo

Старый вопрос, ну уж ответы больно странные, тормозные и запутанные.

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
→ Ссылка
Автор решения: Fox Fox
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 для продолжения...")
→ Ссылка