102

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)

3
  • 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. Commented Apr 16, 2010 at 22:24
  • Related: How to avoid “WindowsError: [Error 5] Access is denied” Commented Jun 13, 2018 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. Commented Nov 10, 2021 at 23:05

5 Answers 5

125

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
Sign up to request clarification or add additional context in comments.

6 Comments

Image
Heh. I just discovered the onerror handler at voidspace.org.uk/downloads/pathutils.py
Image
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 implementation worked, though.
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.
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." docs.python.org/2/library/shutil.html#shutil.rmtree
|
42

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)      

9 Comments

This is nearly right - Windows only supports stat.S_IWRITE (which is what you want anyway) - docs.python.org/library/os.html#os.chmod
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" (docs.python.org/library/stat.html#stat.S_IWRITE), I'm thinking my code is right anyway.
Great, with file paths that are too long this seems like the only way. A recommendation to commit to or change shutil.rmtree perhaps.
This works with stat.S_IWRITE in python 2.7 in windows 10 for read-only files.
Maryam Bahrami: Yes, that is the purpose of the rmtree function. If this is not what you want, why are you using it?
|
30

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

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

2 Comments

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.
Personally, I find it easier just to delete the directory and recreate it (although you do lose the timestamp that way)
3
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

Comments

-8

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.

7 Comments

Calling rm -rf on Windows? I don't think so.
Very strange. I use a unix-like console emulator for Windows (cmder). The subprocess.call approach works when I run the script from that console, but not if I run it from the default "Command Prompt"
Have you tried before downvoting? I confirm it works under Windows
@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.
mh, I think it works for me because I use Cygwin as terminal emulator instead of command prompt
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.