shutil.rmtree删除超深嵌套目录报recursionerror是因旧版python默认递归限制约1000层,其内部递归遍历未转为迭代;python 3.12+已改用迭代实现基本规避该问题,但极深路径或symlink循环仍可能失败。

用 shutil.rmtree 删除嵌套很深的目录时为什么报错?
常见错误是 RecursionError: maximum recursion depth exceeded,尤其在子目录深度超过 1000 层时触发。Python 默认递归限制约 1000 层,而 shutil.rmtree 在旧版本(如 Python 3.11 之前)内部使用递归遍历,不切换为栈式迭代。
- Python 3.12+ 已默认改用迭代实现,基本规避该问题;但若用旧版本或遇到极深路径(如人工构造的嵌套 symlink),仍可能失败
- Windows 下还可能出现
PermissionError: [WinError 5] Access is denied,常因只读文件、进程占用或 ACL 权限导致 - 不要用
os.system("rm -rf")或subprocess调用 shell 命令——跨平台不可靠,且绕过 Python 的异常处理和权限校验
如何让 shutil.rmtree 在权限不足时继续删其余内容?
默认行为是一处失败就中断。要“尽力删除”,需传入 onerror 回调函数,捕获并忽略特定错误(如权限拒绝、路径不存在),但必须区分真正不可恢复的错误(如磁盘 I/O 错误)。
- 回调函数签名必须是
onerror(func, path, exc_info),其中func是触发错误的函数(如os.unlink或os.rmdir) - 只对
PermissionError和FileNotFoundError做pass;对OSError中的errno.EACCES或errno.EPERM显式忽略,其他 errno 应重新抛出 - 示例片段:
def onerror(func, path, _): if func in (os.unlink, os.rmdir) and ( isinstance(sys.exc_info()[1], PermissionError) or sys.exc_info()[1].errno in (errno.EACCES, errno.EPERM) ): return raise
删除前要不要先检查目录是否“安全”?
直接 rmtree 风险极高,尤其当路径来自用户输入或配置文件。必须做最小化校验,而非全量扫描(否则抵消了删除效率)。
- 用
pathlib.Path.resolve()获取绝对路径并规范化,防止../跳出预期范围 - 对比目标路径是否以允许的根目录开头(如
target_path.is_relative_to(allowed_root)),硬性限制作用域 - 避免检查
os.path.exists后再删——存在竞态:检查后被替换为符号链接或挂载点。应直接尝试删除,并捕获NotADirectoryError等异常做响应 - 不建议用
shutil.disk_usage预估大小——对超大目录意义不大,且不解决权限/循环引用等核心问题
遇到符号链接循环或挂载点怎么办?
shutil.rmtree 默认不跟随 symlink,但若目录内含指向父级的 symlink(如 ./a/b/c → ../../),可能陷入无限遍历(尽管新版已加保护)。更危险的是挂载点(mount point),rmtree 会一路删进另一个文件系统。
- 启用
ignore_errors=False(默认),配合自定义遍历逻辑:用os.walk(top, follow_symlinks=False)先收集所有路径,再逐个os.unlink/os.rmdir,过程中用os.stat(path, follow_symlinks=False).st_dev校验是否始终在同一设备上 - 对疑似挂载点,可用
os.stat(path).st_dev != os.stat(parent).st_dev判断——一旦发现,立即中止并报错 - 不依赖
pathlib.Path.is_mount():它在某些文件系统(如 overlayfs)上不准,且无法在遍历中途动态检测
rmtree 变成事故触发器。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











