91

In Python, when running shutil.rmtree over a folder that contains a read-only file, the following exception is printed:

 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 216, in rmtree
   rmtree(fullname, ignore_errors, onerror)
 File "C:\Python26\lib\shutil.py", line 221, in rmtree
   onerror(os.remove, fullname, sys.exc_info())
 File "C:\Python26\lib\shutil.py", line 219, in rmtree
   os.remove(fullname)
WindowsError: [Error 5] Access is denied: 'build\\tcl\\tcl8.5\\msgs\\af.msg'

Looking in File Properties dialog I noticed that af.msg file is set to be read-only.

So the question is: what is the simplest workaround/fix to get around this problem - given that my intention is to do an equivalent of rm -rf build/ but on Windows? (without having to use third-party tools like unxutils or cygwin - as this code is targeted to be run on a bare Windows install with Python 2.6 w/ PyWin32 installed)

BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Sridhar Ratnakumar
  • 81,433
  • 63
  • 146
  • 187
  • 5
    `shutil.rmtree` uses `os.remove` to remove files. `os.remove` removes read-only files just fine (at least on Unix). `os.remove` can't remove file on Windows if it is in use. – jfs Apr 16 '10 at 22:24
  • Related: [How to avoid “WindowsError: \[Error 5\] Access is denied”](https://stackoverflow.com/q/37830326/3357935) – Stevoisiak Jun 13 '18 at 16:03
  • As I experienced, perhaps, this error will be appeared if the directory is open and you run the the code and is related to removing process, not the creation step. – Ali_Sh Nov 10 '21 at 23:05

5 Answers5

118

Check this question out: What user do python scripts run as in windows?

Apparently the answer is to change the file/folder to not be read-only and then remove it.

Here's onerror() handler from pathutils.py mentioned by @Sridhar Ratnakumar in comments:

def onerror(func, path, exc_info):
    """
    Error handler for ``shutil.rmtree``.

    If the error is due to an access error (read only file)
    it attempts to add write permission and then retries.

    If the error is for another reason it re-raises the error.
    
    Usage : ``shutil.rmtree(path, onerror=onerror)``
    """
    import stat
    # Is the error an access error?
    if not os.access(path, os.W_OK):
        os.chmod(path, stat.S_IWUSR)
        func(path)
    else:
        raise
Community
  • 1
  • 1
Justin Peel
  • 46,722
  • 6
  • 58
  • 80
  • 1
    Heh. I just discovered the `onerror` handler at http://www.voidspace.org.uk/downloads/pathutils.py – Sridhar Ratnakumar Apr 16 '10 at 22:27
  • .. discovered that via http://trac.pythonpaste.org/pythonpaste/ticket/359 – Sridhar Ratnakumar Apr 16 '10 at 22:33
  • 2
    Even though the comments for this answer state 'change the file/folder to not be read-only', I still received access denied on read-only folders. [This](http://stackoverflow.com/a/1889686/116047) implementation worked, though. – Pakman Nov 13 '13 at 17:50
  • 3
    A word of warning to those copy-pasting this function as is, move the `import stat` out of the function. I was receiving `RuntimeError: sys.meta_path must be a list of import hooks` when I'd left the import within the function AND the function was within the `__del__` method of a class. – Adam Jul 09 '14 at 08:41
  • 3
    The "else raise" part of the solution will not raise the exception. Coming from the Python documentation: "Exceptions raised by onerror will not be caught." https://docs.python.org/2/library/shutil.html#shutil.rmtree – GDICommander Jun 30 '15 at 19:04
39

I'd say implement your own rmtree with os.walk that ensures access by using os.chmod on each file before trying to delete it.

Something like this (untested):

import os
import stat

def rmtree(top):
    for root, dirs, files in os.walk(top, topdown=False):
        for name in files:
            filename = os.path.join(root, name)
            os.chmod(filename, stat.S_IWUSR)
            os.remove(filename)
        for name in dirs:
            os.rmdir(os.path.join(root, name))
    os.rmdir(top)      
ubershmekel
  • 11,864
  • 10
  • 72
  • 89
Epcylon
  • 4,674
  • 2
  • 26
  • 35
  • 1
    This is nearly right - Windows only supports `stat.S_IWRITE` (which is what you want anyway) - http://docs.python.org/library/os.html#os.chmod – Daniel G Apr 16 '10 at 22:25
  • 1
    I did test that `os.chmod(filename, stat.S_IWUSR)` removed the read-only flag, so it does work on WinXP. And considering this is what the docs say about `stat.S_IWRITE`: "Unix V7 synonym for S_IWUSR" (http://docs.python.org/library/stat.html#stat.S_IWRITE), I'm thinking my code is right anyway. – Epcylon Apr 17 '10 at 13:27
  • Great, with file paths that are too long this seems like the only way. A recommendation to commit to or change shutil.rmtree perhaps. – Anthony Apr 10 '15 at 00:03
  • This works with `stat.S_IWRITE` in python 2.7 in windows 10 for read-only files. – Alper Nov 13 '20 at 12:45
  • Nice, but fails in presence of nested subfolders ... unless you recourse deletion by replacing "os.rmdir(os.path.join(root, name))" with "rmtree(os.path.join(root, name))" – Mario Orlandi Feb 25 '21 at 11:58
  • Have you tried it and gotten an error? The code walks the tree from the bottom up, so all directories are empty by the time we run `os.rmdir` on them, assuming everything else works. – Epcylon Feb 26 '21 at 13:43
  • Be very careful using this function. This function goes to every subfolder and delete the files in there. – Maryam Bahrami May 26 '21 at 06:00
  • Maryam Bahrami: Yes, that is the purpose of the rmtree function. If this is not what you want, why are you using it? – Epcylon May 27 '21 at 08:34
24

Well, the marked solution did not work for me... did this instead:

os.system('rmdir /S /Q "{}"'.format(directory))
HHest
  • 531
  • 1
  • 5
  • 11
AlexeiOst
  • 574
  • 4
  • 13
  • This removed the directory itself. Can you please tell how to remove all dir and files inside a directory? For example if I give path : **myproject/dir1/** then it removes dir1 but I want to delete everything which is under dir1. – Helping Hands Jan 12 '20 at 11:01
  • Personally, I find it easier just to delete the directory and recreate it (although you do lose the timestamp that way) – Mawg says reinstate Monica Apr 06 '22 at 12:42
2
shutil.rmtree(path,ignore_errors=False,onerror=errorRemoveReadonly) 
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

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

RY_ Zheng
  • 3,041
  • 29
  • 36
-6

If you run your script using cygwin, you can use subprocess.call

from subprocess import call
call("rm -rf build/", shell=True)

Of course it only works inside the cygwin/bash emulator.

besil
  • 1,308
  • 1
  • 18
  • 29
  • 2
    Calling `rm -rf` on Windows? I don't think so. – omerfarukdogan Nov 12 '18 at 13:38
  • Very strange. I use a unix-like console emulator for Windows ([cmder](http://cmder.net/)). The `subprocess.call` approach works when I run the script from that console, but not if I run it from the default "Command Prompt" – Dustin Michels Nov 16 '18 at 17:29
  • Have you tried before downvoting? I confirm it works under Windows – besil Nov 26 '18 at 08:07
  • 1
    @besil, yes, `call('rm -rf "C:\\Temp\\tmp7cm15k\\"', shell=True)` results in `'rm' is not recognized as an internal or external command, operable program or batch file.` – omerfarukdogan Jan 18 '19 at 11:04
  • 1
    mh, I think it works for me because I use Cygwin as terminal emulator instead of command prompt – besil Jan 18 '19 at 13:55
  • @besil: qualify your suggestion to running under cygwin or some other windows-ported unix shell. – Richard Jessop Apr 03 '19 at 20:51
  • @RichardJessop just use Cygwin instead of Windows Command Prompt for launching your python script. Cygwin has 'rm' and other unix utilities in the path, so when the py script do 'call system rm', rm is effectively found – besil Apr 08 '19 at 06:57