20

I want to use shutil.rmtree in Python to remove a directory. The directory in question contains a .git control directory, which git marks as read-only and hidden.

The read-only flag causes rmtree to fail. In Powershell, I would do "del -force" to force removal of the read-only flag. Is there an equivalent in Python? I'd really rather not walk the whole tree twice, but the onerror argument to rmtree doesn't seem to retry the operation, so I can't use

def set_rw(operation, name, exc):
    os.chmod(name, stat.S_IWRITE)

shutil.rmtree('path', onerror=set_rw)
BartoszKP
  • 34,786
  • 15
  • 102
  • 130
Paul Moore
  • 6,569
  • 6
  • 40
  • 47
  • 1
    Have you tried removing the file in the `onerror` callback? – Fred Foo Jan 21 '14 at 14:40
  • It is strange that .git is read-only – wim Jan 21 '14 at 14:41
  • I'd have to do a full (recursive) rmtree in the onerror, as it's the .git subdirectory that's readonly, but that might work. And 'git is made hidden and readonly presumably to simulate the Unix behaviour of not displaying dotfiles. I think the behaviour is horrible (hg doesn't do this) but I have to live with it :-( – Paul Moore Jan 21 '14 at 14:43
  • You could change the permissions on the files first, then go through and edit them, I can post a solution for this if you'd like – wnnmaw Jan 21 '14 at 14:43
  • @wnnmaw would that not mean a double-walk of the tree, using os.walk to get everything and force RW mode? If that's what you mean, I know how to do that (as I said, I just want to avoid it if I can). But thanks for the suggestion. – Paul Moore Jan 21 '14 at 14:55
  • 1
    You don't have to walk it twice, just as you walk change the permission, then remove the file. Additionally, I'm not sure what system your on, but if its Windows the permissions are inherited, so you should be able to change the top level and have it cascade down if they're set up right – wnnmaw Jan 21 '14 at 14:58

3 Answers3

43

After more investigation, the following appears to work:

def del_rw(action, name, exc):
    os.chmod(name, stat.S_IWRITE)
    os.remove(name)
shutil.rmtree(path, onerror=del_rw)

In other words, actually remove the file in the onerror function. (You might need to check for a directory in the onerror handler and use rmdir in that case - I didn't need that but it may just be something specific about my problem.

Paul Moore
  • 6,569
  • 6
  • 40
  • 47
0

shutil.rmtree is used to delete directories that are not empty (remove tree).


    import os
    import stat
    import shutil
    def del_ro_dir(dir_name):
        '''Remove Read Only Directories'''
        for (root, dirs, files) in os.walk(dir_name, topdown=True):
            os.chmod(root,
                # For user ...
                stat.S_IRUSR |
                stat.S_IWUSR |
                stat.S_IXUSR |
                # For group ...
                stat.S_IWGRP |
                stat.S_IRGRP |
                stat.S_IXGRP |
                # For other ...
                stat.S_IROTH |
                stat.S_IWOTH |
                stat.S_IXOTH
            )
        shutil.rmtree(dir_name)

    if __name__ == '__main__':
        del_ro_dir('dir_name_here')

To delete a file only, you can use the following code:


    import os
    import stat
    def rmv_rof(file_name):
        '''Remov Read Only Files'''
        if os.path.exists(file_name):
            os.chmod(file_name, stat.S_IWRITE)
            os.remove(file_name)
        else:
            print('The file does not exist.')
    rmv_rof('file_name_here')

You can read detailed information here:

https://docs.python.org/3/library/os.html#os.chmod

https://docs.python.org/3/library/stat.html#module-stat

https://docs.python.org/3/library/shutil.html#rmtree-example

slaff.bg
  • 139
  • 1
  • 6
0

You could just go with the quick-and-dirty method and do subprocess.check_call(["rm", "-rf", filename]). Likely won't work on Windows though.

Kyuuhachi
  • 651
  • 6
  • 15