pathlib.path.rglob() 是专为递归匹配设计的方法,可深度遍历所有子目录查找符合 glob 模式的文件(如 "*.py"),返回 path 对象列表,支持直接调用 .open() 等方法;而 glob() 仅匹配当前层级,不进入子目录。

用 rglob() 而不是 glob() 才能真正递归
很多人写 Path("src").glob("*.py"),结果只扫了当前目录——glob() 不递归,它只匹配一级子路径。真要遍历所有子目录,必须用 rglob(),名字里的 r 就是 recursive 的意思。
实操建议:
-
Path("project").rglob("*.md")会进入project下所有层级,包括project/docs/notes.md和project/tests/subtest/data.md - 通配符位置很关键:
rglob("*.log")匹配任意层级的.log文件;rglob("**/*.log")效果一样,但多写**/是冗余的(rglob默认就等价于带**/) - 如果路径本身含变量,比如
root = Path(user_input),务必先用root.resolve()检查路径是否存在且合法,否则rglob()静默返回空迭代器,不报错也不提醒
区分大小写:Windows 默认不敏感,Linux/macOS 敏感
在 Windows 上跑 Path("src").rglob("*.PY") 可能意外命中 main.py;但在 Linux 上,同样的调用会漏掉所有小写扩展名文件。Python 3.12 的 pathlib 完全依赖底层 OS 的文件系统行为,它自己不做大小写归一化。
稳妥做法:
- 明确用小写写死扩展名,如
"*.py"、"*.toml",避免混用PY/Py - 若必须兼容大小写变体(比如用户可能输
.JPG或.jpg),别靠通配符猜,改用后置过滤:[p for p in Path("img").rglob("*") if p.suffix.lower() in {".jpg", ".jpeg", ".png"}] - 注意:用
suffix而不是suffixes——.tar.gz的suffix是".gz",suffixes才是[".tar", ".gz"];搜压缩包时容易踩这个坑
性能瓶颈常出在深度遍历和大量小文件上
rglob() 是纯 Python 实现的递归,没有操作系统级的优化(不像 find 命令)。当目录树极深或有成千上万个空目录时,它会明显变慢,甚至卡住。
提速技巧:
- 加
max_depth限制(需手动实现):[p for p in Path("logs").rglob("*") if p.is_file() and len(p.parts) - len(Path("logs").parts) ,避免扫到 <code>logs/archive/2020/01/01/...这类超深路径 - 优先用
is_file()过滤,跳过目录:list(Path("src").rglob("*.py"))会把所有匹配路径都生成出来再处理;而[f for f in Path("src").rglob("*.py") if f.is_file()]更早筛掉伪匹配(比如同名目录) - 如果只是检查存在性,别用
list()全加载:next(Path("data").rglob("config.yaml"), None)找到第一个就停,省内存
遇到 PermissionError 别让整个搜索崩掉
访问系统目录(如 /proc、/sys)或权限受限的用户目录时,rglob() 遇到不可读子目录会直接抛 PermissionError,中断整个迭代——它不提供内置的 skip-on-error 机制。
必须自己兜底:
- 用
os.walk()+Path()混合写法更可控,因为os.walk()的onerror参数能捕获并忽略错误 - 纯
pathlib方案:自己写生成器,对每个iterdir()做try/except,再递归子目录。虽然代码多几行,但能精确控制哪些错误可忽略(比如只跳过PermissionError,不跳过FileNotFoundError) - 切记:不要在
rglob()外层包一层try/except PermissionError——那只会捕获第一次失败,后续迭代仍会崩
真正麻烦的不是语法,是当你在 CI 环境里跑 rglob(),突然因为某个临时挂载点权限异常而整条 pipeline 失败,却没留日志说明具体哪个路径触发了错误。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











