shutil.rmtree 删不掉只读文件:ignore_errors=True 最危险

举报
yd_232353574 发表于 2026/11/04 07:55:48 2026/11/04
【摘要】 实测只读文件和只读目录都报 PermissionError WinError 5,两者报错无法区分。ignore_errors=True 不抛异常但目录和文件都残留。给出 onerror 回调改权限重试与手写递归两种解法。

删不掉的目录

有个清理程序报 PermissionError: [WinError 5] 拒绝访问。,路径指向一个目录。

我先怀疑是文件被某个进程占用。加了重试、加了 ignore_errors=True,问题还在,而且目录确实还在那儿。

后来发现根因很朴素:里面的文件是只读的。

复现

三个场景,我都在隔离子进程里跑(Windows 上目录权限的行为受进程环境影响,混着测容易得出错的结论)。

目录里有只读文件

os.chmod(ro_file, stat.S_IREAD)
shutil.rmtree(root)

结果:PermissionError: [WinError 5] 拒绝访问。

目录本身是只读的

os.chmod(subdir, stat.S_IREAD)
shutil.rmtree(root2)

结果:同样是 PermissionError: [WinError 5] 拒绝访问。

两种情况的报错完全一样,光看错误码分不出是哪种。这是个实际用起来别扭的地方。

ignore_errors=True 最坑

shutil.rmtree(root4, ignore_errors=True)

不抛异常了。 但检查一下目录:

  • 根目录还在(root_still_exists=True)
  • 只读文件还在(file_still_exists=True)

ignore_errors=True 的语义是「遇到错误就跳过」,不是「想办法删掉」。它把错误咽下去了,代价是留下半清理的垃圾,而且调用方完全不知道。

这是最危险的一种失败模式:清理程序显示成功,磁盘空间没释放,下次还是不够。我见过这种问题被排查半天,最后发现是某处悄悄加了 ignore_errors=True。

两种能用的解法

带 onerror 回调,失败时改权限再删

import os, stat, shutil

def _force_remove(func, path, exc_info):
    try:
        os.chmod(path, stat.S_IWRITE)
        func(path)
    except Exception:
        pass

shutil.rmtree(root, onerror=_force_remove)

实测结果:OK, exists=False,目录真的删掉了。

onerror 会在每个出错的地方被调用,参数是那个失败的函数(os.remove 或 os.rmdir)和路径。改完权限再重试一次就行。

Python 3.12 起这个回调改名成了 onexc,两者签名略有差别。跨版本兼容的话可以先判断:

kwargs = {'onexc': handler} if sys.version_info >= (3, 12) else {'onerror': handler}
shutil.rmtree(root, **kwargs)

干脆别用 shutil.rmtree

如果目录结构是你自己控制的,手写递归反而更可控:

import os, stat

def force_rmtree(path):
    for root, dirs, files in os.walk(path, topdown=False):
        for name in files:
            p = os.path.join(root, name)
            try:
                os.chmod(p, stat.S_IWRITE)
            except OSError:
                pass
            os.remove(p)
        for name in dirs:
            p = os.path.join(root, name)
            try:
                os.chmod(p, stat.S_IWRITE)
            except OSError:
                pass
            os.rmdir(p)
    os.rmdir(path)

先把所有文件和目录的权限改成可写,再从深往上删。这样不管原本是什么权限组合都能删掉。

代价是要自己处理符号链接、挂载点这些边界情况。收益是行为可预测,不会出现「一半删了」的状态。

怎么判断该用哪种

先确认是不是只读的问题,最快的办法是手动改权限试一次:

os.chmod(some_file, stat.S_IWRITE)   # 改成可写
shutil.rmtree(root)                   # 再试

能删掉就是权限问题,加 onerror 回调解决。

如果文件正被别的进程占用(Windows 上删除被占用的文件也会报拒绝访问),那是另一类问题,得先找到占用进程。用 handle.exe 这类工具,或者重启相关进程。

一句话

shutil.rmtree 删不掉只读文件和只读目录,都报 PermissionError: [WinError 5]。ignore_errors=True 会把错误咽掉但留下垃圾,最危险。加 onerror 回调改权限重试,或者自己写递归删除。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。