1090

I am getting an 'access is denied' error when I attempt to delete a folder that is not empty. I used the following command in my attempt: os.remove("/folder_name").

What is the most effective way of removing/deleting a folder/directory that is not empty?

Georgy
  • 12,464
  • 7
  • 65
  • 73
Amara
  • 13,839
  • 9
  • 32
  • 28

23 Answers23

1746
import shutil

shutil.rmtree('/folder_name')

Standard Library Reference: shutil.rmtree.

By design, rmtree fails on folder trees containing read-only files. If you want the folder to be deleted regardless of whether it contains read-only files, then use

shutil.rmtree('/folder_name', ignore_errors=True)
Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
ddaa
  • 52,890
  • 7
  • 50
  • 59
  • 83
    Note that `rmtree` will fail if there are read-only files: http://stackoverflow.com/questions/2656322/shutil-rmtree-fails-on-windows-with-access-is-denied – Sridhar Ratnakumar Apr 16 '10 at 22:02
  • 12
    This doesn't work for me: Traceback (most recent call last): File "foo.py", line 31, in shutil.rmtree(thistestdir) File "/usr/lib/python2.6/shutil.py", line 225, in rmtree onerror(os.rmdir, path, sys.exc_info()) File "/usr/lib/python2.6/shutil.py", line 223, in rmtree os.rmdir(path) OSError: [Errno 90] Directory not empty: '/path/to/rmtree' – Clayton Hughes Sep 14 '11 at 18:55
  • 4
    Clayton: in all likelihood, a file was added concurrently while rmtree was busy deleting stuff, "rm -rf" would fail the same. – ddaa Sep 14 '11 at 21:43
  • 17
    Anyone know why this functionality is not in the os package? Seems like os.rmdir is quite useless. Any good arguments for why it's implemented this way? – Malcolm Sep 24 '13 at 00:43
  • 24
    @Malcolm The package is a wrapper for OS functions. On [POSIX](http://stackoverflow.com/questions/1780599/i-never-really-understood-what-is-posix) systems [rmdir](http://pubs.opengroup.org/onlinepubs/009695399/functions/rmdir.html) shall fail if the directory is not empty. [Ubuntu](http://manpages.ubuntu.com/manpages/lucid/man2/rmdir.2.html) and [Windows](http://technet.microsoft.com/en-us/library/cc726055(v=ws.10).aspx) are popular examples of POSIX-compliance in this respect. – Iain Samuel McLean Elder Oct 04 '13 at 11:03
  • 2
    @Malcolm It's useful only when you expect the directory to be empty at the time you delete it. You would precede the call to `rmdir` with an explicit list of files to delete. e.g. to make sure you're only deleting files that your program has created. I see it as being analogous to O_EXCL when you open a file -- where there's a precondition posed on the file affected. Also, like Elder says, POSIX – init_js Apr 12 '19 at 17:44
  • AttributeError: module 'shutil' has no attribute 'rmtrees' – Leos313 Feb 21 '20 at 09:45
  • 1
    Note that `ignore_errors=True` does not guarantee the files will be deleted. There could be a PermissionError or WinError, see https://bugs.python.org/issue33240 – Elliott B Sep 03 '20 at 03:16
  • 1
    Helpful note missing from the official documentation: `rmtree` also accepts `pathlib.Path` objects. – Nur L May 17 '21 at 11:33
162

From the python docs on os.walk():

# Delete everything reachable from the directory named in 'top',
# assuming there are no symbolic links.
# CAUTION:  This is dangerous!  For example, if top == '/', it
# could delete all your disk files.
import os
for root, dirs, files in os.walk(top, topdown=False):
    for name in files:
        os.remove(os.path.join(root, name))
    for name in dirs:
        os.rmdir(os.path.join(root, name))
Rodrigue
  • 3,617
  • 2
  • 37
  • 49
kkubasik
  • 3,684
  • 6
  • 25
  • 23
  • 1
    Well, maybe I'm wrong of downmodding. But I can, right now I think it's right. – ddaa Nov 19 '08 at 20:39
  • 4
    @ddaa: While using shutil is definitely the easiest way, there's certainly nothing unpythonic about this solution. I wouldn't have upvoted this answer, but I have this time only to cancel out your downvote :) – Jeremy Cantrell Nov 19 '08 at 21:18
  • 8
    The code itself is pythonic. Using it instead of shutil.rmtree in a real program would be unpythonic: that would be ignoring the "one obvious way of doing it". Anyway, this is semantics, removing the downmod. – ddaa Nov 19 '08 at 21:50
  • 2
    @ddaa Is it unpythonic to want to log every file or dir that is deleted? I am not sure how to do that with shutil.rmtree? – Jonathan Komar Jan 26 '17 at 08:17
  • @macmadness86 Your comment should be a separate question. In short, shutil.rmtree does not do that. It has nothing to do with pythonic or not. – ddaa Jan 26 '17 at 13:32
  • 4
    @ddaa It was food for thought i.e. rhetoric. I know what I'm doing. I just thought you might like to reconsider "the obvious way of doing it" by providing a reason why shutil.rmtree may not be the right "fit". – Jonathan Komar Jan 26 '17 at 15:14
  • comments in `shutil` state that you can copy & modify the functions to adapt them to your needs. For instance in Windows, `shutil.rmtree` doesn't work too well because `os.chmod` sometimes needs to be applied on files (else it fails, unlike on Un*x) – Jean-François Fabre Jul 24 '17 at 08:39
  • On Windows I couldn't get shutil.rmtree to work (tried ignore_errors=True and created a custom onerror function to unset read-only bit) but this method worked fine. – csteel Dec 02 '19 at 19:30
  • `os.rmdir(top)` should be added at the end, assuming the goal was to delete that folder and all its contents. – quoniam Apr 05 '21 at 09:55
143
import shutil
shutil.rmtree(dest, ignore_errors=True)
Siva Mandadi
  • 3,673
  • 2
  • 18
  • 12
  • 1
    This is the correct answer. In my system, even though I set everything in the particular folder to write-read, I get an error when I try to delete. `ignore_errors=True` solves the problem. – Aventinus Oct 07 '16 at 14:36
  • 4
    In my answer the `onerror` parameter is used instead of `ignore_errors`. This way read-only files get deleted rather than ignored. – Dave Chandler May 11 '17 at 11:47
  • Yes, this will not delete files on error. So basically the entire ``rmtree()`` method is ignored. – Juha Untinen Sep 16 '17 at 01:43
  • 3
    This should have been a small edit to the answer accepted 6 years earlier, rather a new answer. I'll do this now. – Jean-François Corbett Feb 23 '18 at 12:01
41

from python 3.4 you may use :

import pathlib

def delete_folder(pth):
    for sub in pth.iterdir():
        if sub.is_dir():
            delete_folder(sub)
        else:
            sub.unlink()
    pth.rmdir() # if you just want to delete the dir content but not the dir itself, remove this line

where pth is a pathlib.Path instance. Nice, but may not be the fastest.

Neuron
  • 5,141
  • 5
  • 38
  • 59
yota
  • 2,020
  • 22
  • 37
23

From docs.python.org:

This example shows how to remove a directory tree on Windows where some of the files have their read-only bit set. It uses the onerror callback to clear the readonly bit and reattempt the remove. Any subsequent failure will propagate.

import os, stat
import shutil

def remove_readonly(func, path, _):
    "Clear the readonly bit and reattempt the removal"
    os.chmod(path, stat.S_IWRITE)
    func(path)

shutil.rmtree(directory, onerror=remove_readonly)
Pod
  • 3,938
  • 2
  • 37
  • 45
Dave Chandler
  • 651
  • 6
  • 17
12

Base on kkubasik's answer, check if folder exists before remove, more robust

import shutil
def remove_folder(path):
    # check if folder exists
    if os.path.exists(path):
         # remove if exists
         shutil.rmtree(path)
    else:
         # throw your exception to handle this special scenario
         raise XXError("your exception") 
remove_folder("/folder_name")
Charles Chow
  • 1,027
  • 12
  • 26
  • 7
    this introduces a possible race condition – Corey Goldberg Apr 30 '17 at 01:31
  • 1
    according to [most-pythonic-way-to-delete-a-file-which-may-not-exist](https://stackoverflow.com/questions/10840533/most-pythonic-way-to-delete-a-file-which-may-not-exist), it's preferable to `try` remove and handle `except` than call `exists()` first – TT-- Feb 02 '18 at 01:45
9
import os
import stat
import shutil

def errorRemoveReadonly(func, path, exc):
    excvalue = exc[1]
    if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
        # change the file to be readable,writable,executable: 0777
        os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)  
        # retry
        func(path)
    else:
        # raiseenter code here

shutil.rmtree(path, ignore_errors=False, onerror=errorRemoveReadonly) 

If ignore_errors is set, errors are ignored; otherwise, if onerror is set, it is called to handle the error with arguments (func, path, exc_info) where func is os.listdir, os.remove, or os.rmdir; path is the argument to that function that caused it to fail; and exc_info is a tuple returned by sys.exc_info(). If ignore_errors is false and onerror is None, an exception is raised.enter code here

Community
  • 1
  • 1
RY_ Zheng
  • 3,041
  • 29
  • 36
  • According to the [docs](https://docs.python.org/2/library/shutil.html?highlight=shutil#shutil.rmtree), _Exceptions raised by onerror will not be caught_ so I'm not sure your _raise enter code here_ means anything. – kmarsh Nov 03 '15 at 16:30
  • -1. This seems overcomplicated compared to Dave Chandler's answer. Also, if we want to remove readonly, we don't need to make the files executable. – idbrii Mar 01 '19 at 17:47
8

I'd like to add a "pure pathlib" approach:

from pathlib import Path
from typing import Union

def del_dir(target: Union[Path, str], only_if_empty: bool = False):
    """
    Delete a given directory and its subdirectories.

    :param target: The directory to delete
    :param only_if_empty: Raise RuntimeError if any file is found in the tree
    """
    target = Path(target).expanduser()
    assert target.is_dir()
    for p in sorted(target.glob('**/*'), reverse=True):
        if not p.exists():
            continue
        p.chmod(0o666)
        if p.is_dir():
            p.rmdir()
        else:
            if only_if_empty:
                raise RuntimeError(f'{p.parent} is not empty!')
            p.unlink()
    target.rmdir()

This relies on the fact that Path is orderable, and longer paths will always sort after shorter paths, just like str. Therefore, directories will come before files. If we reverse the sort, files will then come before their respective containers, so we can simply unlink/rmdir them one by one with one pass.

Benefits:

  • It's NOT relying on external binaries: everything uses Python's batteries-included modules (Python >= 3.6)
    • Which means that it does not need to repeatedly start a new subprocess to do unlinking
  • It's quite fast & simple; you don't have to implement your own recursion
  • It's cross-platform (at least, that's what pathlib promises in Python 3.6; no operation above stated to not run on Windows)
  • If needed, one can do a very granular logging, e.g., log each deletion as it happens.
pepoluan
  • 6,132
  • 4
  • 46
  • 76
  • can you provide also an usage example eg. del_dir(Path())? Thanks – lcapra Aug 28 '19 at 08:18
  • @lcapra Simply call it with the directory to delete as the first arg. – pepoluan Aug 30 '19 at 07:11
  • 1
    "It's fast and memory-efficient: No recursion stack, no need to start a subprocess" - that's actually not so true. There is still recursion going on in the recursive globbing. It's also not memory-efficient because you generate two lists containing the paths of all files and folders: the `sorted` builtin first generates a list of the items returned by the `glob` generator, and then generates a new list with the items sorted. Depending on the number of files, that could lead to significant memory consumption. Oh, and you're introducing a sort with `n log n` time complexity. – danzel Oct 28 '20 at 10:23
  • @danzel you're technically correct. I'll edit my answer to not mislead. – pepoluan Oct 31 '20 at 08:37
  • @danzel that said, I don't think the sorting will be slower than repeatedly starting a subprocess to run shell commands using `os.system` or `subprocess.run`. Also the memory needed to maintain a list + a sorted list is probably smaller than the memory needed to start a subprocess and run it. YMMV – pepoluan Oct 31 '20 at 08:44
7

if you are sure, that you want to delete the entire dir tree, and are no more interested in contents of dir, then crawling for entire dir tree is stupidness... just call native OS command from python to do that. It will be faster, efficient and less memory consuming.

RMDIR c:\blah /s /q 

or *nix

rm -rf /home/whatever 

In python, the code will look like..

import sys
import os

mswindows = (sys.platform == "win32")

def getstatusoutput(cmd):
    """Return (status, output) of executing cmd in a shell."""
    if not mswindows:
        return commands.getstatusoutput(cmd)
    pipe = os.popen(cmd + ' 2>&1', 'r')
    text = pipe.read()
    sts = pipe.close()
    if sts is None: sts = 0
    if text[-1:] == '\n': text = text[:-1]
    return sts, text


def deleteDir(path):
    """deletes the path entirely"""
    if mswindows: 
        cmd = "RMDIR "+ path +" /s /q"
    else:
        cmd = "rm -rf "+path
    result = getstatusoutput(cmd)
    if(result[0]!=0):
        raise RuntimeError(result[1])
P M
  • 837
  • 1
  • 10
  • 17
  • 35
    -1. The whole point of using `shutil.rmdir` is to insulate you from the type of operating system. – mtrw Dec 22 '10 at 08:32
  • 4
    I understand the concept, but when one is well aware about the fact that (s)he want to delete the folder entirely, then what's the point of crawling the entire file tree ? shutil.rmdir specifically call os.listdir(), os.path.islink() etc etc.. some checks which are not really always needed, as all needed is to unlink the file system node. Beside on some build systems, like MSWindows for MSAuto/WinCE development, then shtuil.rmdir will fail almost always, as MSAuto batch based development locks some wierd build files on unsuccessful exit, and only rmdir /S/Q or restart is helpful to clean them. – P M Dec 23 '10 at 08:10
  • 1
    @PM `what's the point of crawling the entire file tree ? shutil.rmdir specifically call os.listdir(), os.path.islink() etc etc..` - Well, yeah. There's no other way to do it. `rm` and `RMDIR` do the exact same thing. – Izkata Feb 10 '12 at 21:01
  • 2
    yep, just rm is closer to kernel, using less time, memory and cpu ..... and as i said, the reason for me to use this method was because of locks left behind by MSAuto batch build scripts ... – P M Feb 11 '12 at 18:25
  • 3
    Yes, but using shutil makes the code cross-platform and abstracts away platform details. – xshoppyx Aug 03 '13 at 02:23
  • 1
    I just run into problem when a dir to be deleted contained subdir with 000 rights. shutil.rmdir failed here. rm -rf worked. Obviosly shutil.rmdir is not translated into rm -rf on linux. – user2389519 Oct 26 '13 at 20:18
  • 2
    I do not think this answer should be down voted below 1 as it provides a very nice reference for a work around for certain situations in which a reader might be interested in. I enjoy having multiple methods posted with them ranked in order. So even though I do not need to use this I now know it can be done and how. – kmcguire Jun 06 '14 at 11:45
  • I'm not a 100% sure, but won't the native OS commands (eg. rm -rf) also internally have to walk the entire directory tree? If it doesn't do that, then all the files won't be deleted. – kapad Oct 14 '19 at 20:32
6

Just some python 3.5 options to complete the answers above. (I would have loved to find them here).

import os
import shutil
from send2trash import send2trash # (shutil delete permanently)

Delete folder if empty

root = r"C:\Users\Me\Desktop\test"   
for dir, subdirs, files in os.walk(root):   
    if subdirs == [] and files == []:
           send2trash(dir)
           print(dir, ": folder removed")

Delete also folder if it contains this file

    elif subdirs == [] and len(files) == 1: # if contains no sub folder and only 1 file 
        if files[0]== "desktop.ini" or:  
            send2trash(dir)
            print(dir, ": folder removed")
        else:
            print(dir)

delete folder if it contains only .srt or .txt file(s)

    elif subdirs == []: #if dir doesn’t contains subdirectory
        ext = (".srt", ".txt")
        contains_other_ext=0
        for file in files:
            if not file.endswith(ext):  
                contains_other_ext=True
        if contains_other_ext== 0:
                send2trash(dir)
                print(dir, ": dir deleted")

Delete folder if its size is less than 400kb :

def get_tree_size(path):
    """Return total size of files in given path and subdirs."""
    total = 0
    for entry in os.scandir(path):
        if entry.is_dir(follow_symlinks=False):
            total += get_tree_size(entry.path)
        else:
            total += entry.stat(follow_symlinks=False).st_size
    return total


for dir, subdirs, files in os.walk(root):   
    If get_tree_size(dir) < 400000:  # ≈ 400kb
        send2trash(dir)
    print(dir, "dir deleted")
JinSnow
  • 1,553
  • 4
  • 27
  • 49
6

Ten years later and using Python 3.7 and Linux there are still different ways to do this:

import subprocess
from pathlib import Path

#using pathlib.Path
path = Path('/path/to/your/dir')
subprocess.run(["rm", "-rf", str(path)])

#using strings
path = "/path/to/your/dir"
subprocess.run(["rm", "-rf", path])

Essentially it's using Python's subprocess module to run the bash script $ rm -rf '/path/to/your/dir as if you were using the terminal to accomplish the same task. It's not fully Python, but it gets it done.

The reason I included the pathlib.Path example is because in my experience it's very useful when dealing with many paths that change. The extra steps of importing the pathlib.Path module and converting the end results to strings is often a lower cost to me for development time. It would be convenient if Path.rmdir() came with an arg option to explicitly handle non-empty dirs.

RodogInfinite
  • 71
  • 1
  • 4
  • I also switched to this approach, because I ran into issues with `rmtree` and hidden folders like `.vscode`. This folder was detected as text-file and the error told me that this file was `busy` and could not be deleted. – Daniel Eisenreich Apr 03 '20 at 08:54
3
def deleteDir(dirPath):
    deleteFiles = []
    deleteDirs = []
    for root, dirs, files in os.walk(dirPath):
        for f in files:
            deleteFiles.append(os.path.join(root, f))
        for d in dirs:
            deleteDirs.append(os.path.join(root, d))
    for f in deleteFiles:
        os.remove(f)
    for d in deleteDirs:
        os.rmdir(d)
    os.rmdir(dirPath)
amazingthere
  • 992
  • 8
  • 10
3

Recursion-based, pure pathlib solution:

from pathlib import Path

def remove_path(path: Path):
    if path.is_file() or path.is_symlink():
        path.unlink()
        return
    for p in path.iterdir():
        remove_path(p)
    path.rmdir()

Supports Windows and symbolic links

Gustavo Bezerra
  • 9,984
  • 4
  • 40
  • 48
2

If you don't want to use the shutil module you can just use the os module.

from os import listdir, rmdir, remove
for i in listdir(directoryToRemove):
    os.remove(os.path.join(directoryToRemove, i))
rmdir(directoryToRemove) # Now the directory is empty of files
Ayush
  • 479
  • 2
  • 9
  • 24
2

To delete a folder even if it might not exist (avoiding the race condition in Charles Chow's answer) but still have errors when other things go wrong (e.g. permission problems, disk read error, the file isn't a directory)

For Python 3.x:

import shutil

def ignore_absent_file(func, path, exc_inf):
    except_instance = exc_inf[1]
    if isinstance(except_instance, FileNotFoundError):
        return
    raise except_instance

shutil.rmtree(dir_to_delete, onerror=ignore_absent_file)

The Python 2.7 code is almost the same:

import shutil
import errno

def ignore_absent_file(func, path, exc_inf):
    except_instance = exc_inf[1]
    if isinstance(except_instance, OSError) and \
        except_instance.errno == errno.ENOENT:
        return
    raise except_instance

shutil.rmtree(dir_to_delete, onerror=ignore_absent_file)
Eponymous
  • 6,143
  • 4
  • 43
  • 43
2

For Windows, if directory is not empty, and you have read-only files or you get errors like

  • Access is denied
  • The process cannot access the file because it is being used by another process

Try this, os.system('rmdir /S /Q "{}"'.format(directory))

It's equivalent for rm -rf in Linux/Mac.

Kartik Raj
  • 153
  • 6
2

It helps to delete a directory with all files and folders

import os


def rrmdir(path):
    for entry in os.scandir(path):
        if entry.is_dir():
            rrmdir(entry)
        else:
            os.remove(entry)
    os.rmdir(path)
1

With os.walk I would propose the solution which consists of 3 one-liner Python calls:

python -c "import sys; import os; [os.chmod(os.path.join(rs,d), 0o777) for rs,ds,fs in os.walk(_path_) for d in ds]"
python -c "import sys; import os; [os.chmod(os.path.join(rs,f), 0o777) for rs,ds,fs in os.walk(_path_) for f in fs]"
python -c "import os; import shutil; shutil.rmtree(_path_, ignore_errors=False)"

The first script chmod's all sub-directories, the second script chmod's all files. Then the third script removes everything with no impediments.

I have tested this from the "Shell Script" in a Jenkins job (I did not want to store a new Python script into SCM, that's why searched for a one-line solution) and it worked for Linux and Windows.

Alexander Samoylov
  • 2,358
  • 2
  • 25
  • 28
  • With `pathlib`, you can combine the first two steps into one: `[p.chmod(0o666) for p in pathlib.Path(_path_).glob("**/*")]` – pepoluan Jun 16 '19 at 23:25
1

You can try the code below to delete files or folders regardless of whether them being empty or non-empty.

import shutil
import os

directory = "path/to/the/root/folder"
files_in_directory = os.listdir(directory)

for file in files_in_directory:
    try:
        path_to_file_or_folder = os.path.join(directory, file)
        shutil.rmtree(path_to_file_or_folder)
    except:
        os.unlink(path_to_file_or_folder)
0

You can use os.system command for simplicity:

import os
os.system("rm -rf dirname")

As obvious, it actually invokes system terminal to accomplish this task.

0

I have found a very easy way to Delete any folder(Even NOT Empty) or file on WINDOWS OS.

os.system('powershell.exe  rmdir -r D:\workspace\Branches\*%s* -Force' %CANDIDATE_BRANCH)
seremet
  • 175
  • 2
  • 11
0

In my case the only way to delete was by using all possibilities because my code was supposed to run either by cmd.exe or powershell.exe. If it is your case, just create a function with this code and you will be fine:

        #!/usr/bin/env python3

        import shutil
        from os import path, system
        import sys

        # Try to delete the folder ---------------------------------------------
        if (path.isdir(folder)):
            shutil.rmtree(folder, ignore_errors=True)

        if (path.isdir(folder)):
            try:
                system("rd -r {0}".format(folder))
            except Exception as e:
                print("WARN: Failed to delete => {0}".format(e),file=sys.stderr)

        if (path.isdir(self.backup_folder_wrk)):
            try:
                system("rd /s /q {0}".format(folder))
            except Exception as e:
                print("WARN: Failed to delete => {0}".format(e),file=sys.stderr)

        if (path.isdir(folder)):
            print("WARN: Failed to delete {0}".format(folder),file=sys.stderr)
        # -------------------------------------------------------------------------------------

rick
  • 479
  • 5
  • 14
-3

Try this, If you know the full path:

import os
os.removedirs("your-full-path")
getimad
  • 1
  • 2