8

I am trying to remove the empty folders of a directory.

def remove_empty_dir(path):
    try:
        os.rmdir(path)
    except OSError:
        pass

def remove_empty_dirs(path):
    for root, dirnames, filenames in os.walk(path):
        for dirname in dirnames:
            remove_empty_dir(os.path.realpath(os.path.join(root, dirname)))

remove_empty_dirs(path)

I have also tried with:

import shutil
shutil.rmtree(path)

But that removes everything, even those folders with contents. The problem is that I need to do it from inside to outside, this way if I have:

root_folder
  child_folder1
    grandchild_folder1.1 (empty)
  child_folder2
    granchild_folder2.1
    granchild_folder2.2 (empty)

The program should delete grandchild_folder1.1, child_folder1 and child_folder2.2, but not the rest.

3 Answers 3

11

os.walk accepts optional topdown parameter (default: True).

By providing topdown=False, you can iterative from child directories first.

def remove_empty_dirs(path):
    for root, dirnames, filenames in os.walk(path, topdown=False):
        for dirname in dirnames:
            remove_empty_dir(os.path.realpath(os.path.join(root, dirname)))
Sign up to request clarification or add additional context in comments.

5 Comments

That is great! But I forgot to say that my function was not working, so I have to change something else.
Done! I had to change remove_empty_dir(os.path.realpath(dirname)) and write remove_empty_dir(os.path.realpath(os.path.join(root, dirname))) instead. Thank you!
Should remove_empty_dir()be os.rmdir() ? @falsetru
@alper, No, it should be remove_empty_dir. It came from OP's code. os.rmdir(..) will raise an OSError for non-empty directory.
Ah its on the question itself, it wasn't clear. I can also check the folder's status as empty or not using: next(os.scandir(path), None) is None and apply os.rmdir() since os.walk() builds a list of the directory contents @falsetru
5

Using the pathlib library in Python 3 this is a one-liner (other than includes). In the snippet below target_path is a string of the root of the tree you want to clean up:

from pathlib import Path
import os

[os.removedirs(p) for p in Path(target_path).glob('**/*') if p.is_dir() and len(list(p.iterdir())) == 0]

To make it a little less dense and easier to follow, this is the same thing written without the list comprehension

for p in Path(target_path).glob('**/*'):
    if p.is_dir() and len(list(p.iterdir())) == 0:
        os.removedirs(p)

The interesting feature here is the if statement filters for empty directories that are leaves on the filesystem tree. os.removedirs() deletes all empty folders in above an empty leaf. If there are several empty leaves on a branch, deleting the last empty leaf will cause os.removedirs() to walk up the branch. So all empty dirs are gone in a single iteration of the loop with no recursion necessary!

1 Comment

The problem with glob(*/) function is that when os.removedirs() removes folder that glob() is about to access, it generated an exception.
1
def remove_empty_dirs(dir_path):
    p1 = subprocess.Popen(["find", dir_path, "-type", "d", "-empty", "-print0"], stdout=subprocess.PIPE,)
    p2 = subprocess.Popen(["xargs", "-0", "-r", "rmdir"], stdin=p1.stdout, stdout=subprocess.PIPE)
    p1.stdout.close()
    p2.communicate()

1 Comment

A comment would be good to describe why you suggest an alternative solution, but I like that you don't use the "os" library, like os.rmdir() or similar. I have experienced before that it can cause zombie processes so I try to avoid that. Therefore this solution is relevant for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.