shutil.rmtree 删除非空目录遇 PermissionError 时应传入 onerror 或 Python 3.12+ 的 onexc 回调函数,先用 os.chmod 改只读权限再重试;os.removedirs 仅删除全空目录链,而 rmtree 强制递归清空。

shutil.rmtree 删除非空目录时 PermissionError 怎么办
直接用 shutil.rmtree 删系统保护目录、正在被占用的文件夹,或者权限不足的路径(比如 Windows 上的只读文件),会抛 PermissionError: [WinError 5] 拒绝访问 或 OSError: [Errno 13]。这不是函数写错了,是操作系统在拦你。
解决思路不是硬刚权限,而是让 rmtree 在出错时主动“低头”:把只读文件先改成可写,再删;遇到正在使用的文件,不强删,而是跳过或报错提示。
- 传入
onerror回调函数,捕获PermissionError后调用os.chmod(path, stat.S_IWRITE)改权限,再重试一次 - Windows 下尤其注意:某些编辑器(如 VS Code、PyCharm)会锁住 .git/ 或 __pycache__/ 目录,删前最好关掉项目
- 别对
/、C:\、os.environ['USERPROFILE']这类高危路径写死调用rmtree,加一层路径合法性校验更稳妥
shutil.rmtree 和 os.removedirs 的核心区别在哪
os.removedirs 只能删「空目录链」——比如 a/b/c,必须 c 空、b 空、a 也空,它才一路删上去;只要中间某层有文件或子目录,就直接失败。而 shutil.rmtree 是真·递归清空,不管深浅、不管有没有内容,删完拉倒。
所以别指望 removedirs 替代 rmtree,它连一个含 .DS_Store 的空文件夹都删不掉(因为那不算“空”)。
立即学习“Python免费学习笔记(深入)”;
- 想安全删空目录结构(比如清理临时层级),用
os.removedirs - 要彻底清掉构建产物、缓存目录、测试输出,必须用
shutil.rmtree -
rmtree不返回值,删失败就抛异常;removedirs成功返回None,失败也抛异常,但错误类型不同(常是OSError)
Python 3.12+ 中 shutil.rmtree 的 onexc 参数替代 onerror
从 Python 3.12 开始,onerror 被标记为 deprecated,官方推荐用 onexc——参数名变了,签名也更明确:onexc(func, path, exc_info),其中 func 是触发异常的底层函数(比如 os.rmdir),path 是出问题的路径,exc_info 是异常三元组。
老代码里写的 onerror=lambda func, path, _ : ... 在 3.12+ 仍能跑,但会打 warning;新项目建议直接切 onexc,逻辑更清晰,也方便区分是 os.remove 失败还是 os.rmdir 失败。
- 旧写法:
shutil.rmtree(path, onerror=handle_error) - 新写法:
shutil.rmtree(path, onexc=handle_exc),且handle_exc必须接收三个参数 - 如果还要兼容 3.11 及更早版本,得做运行时判断:
kwargs = {'onexc': h} if sys.version_info >= (3, 12) else {'onerror': h}
删除前要不要先检查目录是否存在或是否为目录
不需要额外调 os.path.exists 或 os.path.isdir。因为 shutil.rmtree 本身就会检查:path 不存在 → 抛 FileNotFoundError;path 存在但不是目录 → 抛 NotADirectoryError(Python 3.9+)或更早的 OSError。
与其写两行检查再删,不如直接 try-except 更干净。而且检查和删除之间存在竞态条件:比如你查完存在,下一秒就被别人删了,到 rmtree 执行时还是报错。
- 想静默忽略“不存在”,就 catch
FileNotFoundError - 想区分“不是目录”和“没权限”,就分别捕获
NotADirectoryError和PermissionError - 不要用
os.path.isdir(path) and shutil.rmtree(path),多此一举还埋雷










