abisko
abisko

Reputation: 743

How to find and delete folders that are empty / contains empty folders only

If folder X is empty, I would like to delete X.

If folder Y only contains folder X and other folders that are empty, I would like to delete Y.

If folder Z only contains folders like X and/or Y, I would like to delete Z.

How do I do this recursively for anything under a specific dir, with Python?

I tried something like the following, but it is only able to identify folders like X, not folders like Y or Z.

from pathlib import Path

folder = '/home/abc'
for path in Path(folder).glob("**/*"):
    if path.is_dir() and len(list(path.iterdir())) == 0:
        logger.info(f"remove {path}")

Upvotes: 0

Views: 658

Answers (3)

BowlOfRed
BowlOfRed

Reputation: 349

os.rmdir() will fail on any directory with contents. So one method here is to just rmdir every directory from bottom to top while suppressing the OSError exception which is thrown when attempting to remove a non-empty directory. All empty ones will be removed, all with contents will remain. Technically, checking if a directory is empty before attempting the removal is a race condition (though typically, a harmless one).

Let's take this filesystem with 2 files in it.

testtree/
├── a
│   └── aa
│       └── filea
├── b
│   ├── bb
│   └── fileb
└── c
    └── cc

And run this:

import os
from pathlib import Path
from contextlib import suppress

for root,dirs,_ in os.walk("testtree", topdown=False):
    for d in dirs:
        with suppress(OSError):
            os.rmdir(Path(root,d))

Then the tree is transformed to

testtree/
├── a
│   └── aa
│       └── filea
└── b
    └── fileb

bb, cc, and c were all removed.

Upvotes: 1

MarkM
MarkM

Reputation: 857

May be a bit verbose, but this seems to do the job.

def dir_empty(path):
    empty = True
    for item in path.glob('*'):
        if item.is_file():
            empty = False
        if item.is_dir() and not dir_empty(item):
            empty = False
    if empty:
        path.rmdir()  # Remove if you just want to have the result
    return empty

from pathlib import Path
dir_empty(Path('Z'))

Upvotes: 1

Code-Apprentice
Code-Apprentice

Reputation: 83577

The problem here is that glob("**/*") appears to do a preorder traversal. In other words, it returns a parent folder before returning any children.

You need to do a postorder traversal instead. In your example, if you delete a folder like X, then Y just becomes the same as X.

You could do this with manual recursion. But if you want to use glob(), you need to reverse the items returned. You can do this with reversed(Path(folder.glob("**/*")).

Upvotes: 0

Related Questions