17

This is the folder tree:

FOLDER\\
       \\1\\file
       \\2\\file
       \\3\\
       \\4\\file

The script should scan (loop) for each folder in FOLDER and check if the sub-folders are empty or not. If they are, they must be deleted.

My code, until now, is this:

folders = ([x[0] for x in os.walk(os.path.expanduser('~\\Desktop\\FOLDER\\DIGITS\\'))])
folders2= (folders[1:])

This scan for folders and, using folders2 begin from the firs folder in DIGITS. In DIGITS there are numbered directories: 1,2,3,4,etc

Now what? Tried using os.rmdir but it gives me an error, something about string. In fact, folders2 is a list, not a string, just saying...

Gavriel Cohen
  • 4,355
  • 34
  • 39
BlueTrack
  • 442
  • 3
  • 7
  • 19

12 Answers12

16

Not sure what kind of error you get, this works perfectly for me:

import os

root = 'FOLDER'
folders = list(os.walk(root))[1:]

for folder in folders:
    # folder example: ('FOLDER/3', [], ['file'])
    if not folder[2]:
        os.rmdir(folder[0])
taras
  • 3,579
  • 3
  • 26
  • 27
  • I mean in a for loop. Maybe I was wrong...Anyway, the answer do the job perfectly. – BlueTrack Nov 03 '17 at 10:47
  • 2
    yep, this solution will not work in a few cases like: 1) recursion (if there are folders deeper than 1 level) 2) if folder has inner folders but has not files in it – taras Nov 03 '17 at 10:49
  • 2
    Which is what I want for this specific case. Thanks. – BlueTrack Nov 03 '17 at 10:50
  • 2
    Although this answers the question, has many issues! 1. What if root directory is empty? 2. What happens if a directory had all sub directories only? 3. You are converting the generator to a list just to skip the first element which you don't need to. – sidcha Aug 27 '21 at 12:05
15

You can remove all empty folders and subfolder with the following snippet.

import os


def remove_empty_folders(path_abs):
    walk = list(os.walk(path_abs))
    for path, _, _ in walk[::-1]:
        if len(os.listdir(path)) == 0:
            os.remove(path)

if __name__ == '__main__':
    remove_empty_folders("your-path")
Iván B.
  • 175
  • 1
  • 5
14

A few things to expand on other answers:

If you use os.walk(topdown=False), it goes in reverse order, so you encounter the child directories before the parents. Then if you track which directories you've deleted, you can delete parent directories recursively.

import os


def delete_empty_folders(root):

    deleted = set()
    
    for current_dir, subdirs, files in os.walk(root, topdown=False):

        still_has_subdirs = any(
            _ for subdir in subdirs
            if os.path.join(current_dir, subdir) not in deleted
        )
    
        if not any(files) and not still_has_subdirs:
            os.rmdir(current_dir)
            deleted.add(current_dir)

    return deleted

rcgale
  • 141
  • 1
  • 2
  • 1
    This is the best answer, it is the only answer that does not involve make a list of potentially millions of entries. – rayzinnz Nov 11 '22 at 17:09
6

Delete folder only if it is empty:

import os
import shutil

if len(os.listdir(folder_path)) == 0: # Check if the folder is empty
    shutil.rmtree(folder_path) # If so, delete it
Gavriel Cohen
  • 4,355
  • 34
  • 39
  • 1
    You will want to add `import shutil` to the imports in your answer – Moshe Slavin Aug 27 '20 at 08:23
  • 2
    You can shoot yourself in a leg with that, since os.listdir does not always correctly return files on network shares (e.g. "hang" nfs being in a process of re-mounting will show 0 in the moment of unmount), shutil.rmtree might leave you without files in those cases... – Anubioz Feb 09 '21 at 16:56
  • Hey @Anubioz, I did not come across the case as you describe, but I learned something, thanks :) – Gavriel Cohen Feb 09 '21 at 17:53
3
import os

directory = r'path-to-directory'

for entry in os.scandir(directory):
    if os.path.isdir(entry.path) and not os.listdir(entry.path) :
        os.rmdir(entry.path)

Here you can find a good introduction/explanation to scandir

  • Would you mind editing your answer to help readers understand why this approach might be preferable over the accepted answer (or, really, any of the previous three answers)? – Jeremy Caney Jun 01 '20 at 03:54
  • 2
    If you are going to use scandir, I think you don't get as much benefit if you use os.path.isdir() instead of entry.is_dir() - the whole point is that it is faster by not needing additional stat() calls, and os.path.isdir() is doing that stat() call, isn't it? – AnotherHowie Sep 20 '20 at 09:54
  • This only removes empty directories directly within the target directory but does not search the directory tree for empty directories to remove. – ChungaBunga Jun 02 '22 at 22:56
3

Almost what Iván B. said but with a minor change that worked for me

import os, shutil
path_abs=('YourPath')
walk = list(os.walk(path_abs))
for path, _, _ in walk[::-1]:
    if len(os.listdir(path)) == 0:
        os.rmdir(path)
Nuno André
  • 4,739
  • 1
  • 33
  • 46
xoelinho
  • 31
  • 2
2
#LOOP THROUGH ALL SUBFOLDERS FIRST

import os
root = os.getcwd() #CHANGE THIS TO PATH IF REQUIRED
folders = sorted(list(os.walk(root))[1:],reverse=True)
for folder in folders:
    try:
        os.rmdir(folder[0])
    except OSError as error: 
        print("Directory '{}' can not be removed".format(folder[0])) 

This should go through all the sub-folders first and remove if empty folders. Not deleting non empty folders that a parent folders.

I know it an old post but I am old myself !!!

user1184628
  • 61
  • 1
  • 6
1

For empty folders deletion you can use this snippet.

import os


def drop_empty_folders(directory):
    """Verify that every empty folder removed in local storage."""

    for dirpath, dirnames, filenames in os.walk(directory, topdown=False):
        if not dirnames and not filenames:
            os.rmdir(dirpath)
Insspb
  • 193
  • 10
  • 1
    This does not work for folder with empty child folder. Child folder gets deleted but parent - even though is evaluated after deleting child - still sees the child folder in dirnames. Although topdown=False is nice. – P4C May 25 '20 at 13:32
1

This answer fixes issues in the current accepted answer. The major issue there is that the condition to remove a directory has to be not filenames and not dirnames.

import os

def remove_empty_directories(root):
    for dirpath, dirnames, filenames in os.walk(root):
        if not filenames and not dirnames:
            os.rmdir(dirpath)
sidcha
  • 649
  • 5
  • 18
  • This seems to duplicate [an earlier answer from 2020](https://stackoverflow.com/a/61925409/874188) with a comment pointing out a bug in it. – tripleee Apr 28 '22 at 15:08
1

If you are on UNIX system, you can use the "find" command in a python script with subprocess.
You search for directories with : -type d
You select empty directories with : -empty
You delete these directories with : -delete
You launch the command with subprocess.run

import subprocess

command = "find {} -type d -empty -delete".format(folder_path)  
subprocess.run(command, shell=True)

It is just 3 lines code.

Following @tripleee's advice, the code can be written like this :

import subprocess
path = "your path here"
command = ["find", path, "-type", "d", "-empty", "-delete"]
subprocess.run(command)
Community
  • 1
  • 1
McBeth
  • 11
  • 3
  • That's rather silly. Probably see also [Actual meaning of `shell=True` in subprocess](https://stackoverflow.com/questions/3172470/actual-meaning-of-shell-true-in-subprocess) (then refactor to take it out) – tripleee Apr 28 '22 at 15:03
  • @tripleee Thank you for your comment. You are surely right. But if you don't add "shell=True" in the subprocess.run command, the script crashes and returns a "FileNotFoundError" error. – McBeth Apr 30 '22 at 21:33
  • That's why I added the comment about refactoring. `subprocess.run("echo {} bar".format(foo), shell=True)` refactors to `subproces.run(["echo", foo, "bar"])`. This too is a common FAQ, and covered in the linked question. – tripleee May 01 '22 at 06:47
  • @tripleee Understood. Thank you for your advice. – McBeth May 02 '22 at 07:18
0

I suggest that you call this function, It is designed with an optional argument. If you set in_place to False it will not remove the folders it will just return them as a list. Meanwhile, if you set it to True the function will remove the empty folders regardless of the subdirectories tree depth.

P.S. In case you need a depth limitation you can analyze it by elements after root path.

import os
import shutil

def purge_dir(dir, in_place=False):

    deleted = []
    dir_tree = list(os.walk(dir, topdown=False))

    for tree_element in dir_tree:
        sub_dir = tree_element[0]
        is_empty = not len(os.listdir(sub_dir))
        if is_empty:
            deleted.append(sub_dir)

    if in_place:
        list(map(os.rmdir, deleted))

    return deleted

I suggest that you keep in_place set to False return back the empty folders and use it as you like since it is safer especially for security critical applications.

haddagart
  • 68
  • 1
  • 8
0

This one fixes the problem with all the other answers. It avoids giving you a PermissionDenied error which stops the program from deleting folders.

import os
import shutil

def remove_empty_folders(path_abs):
    walk = list(os.walk(path_abs))
    for path, _, _ in walk[::-1]:
        if len(os.listdir(path)) == 0:
            shutil.rmtree(path)