os.walk()是python递归遍历目录最稳妥方式,返回(root, dirs, files)三元组;过滤后缀应使用pathlib.path(file).suffix.lower()统一转小写比对,避免多点扩展名误判,路径拼接须用os.path.join(root, file)确保跨平台安全。

用 os.walk() 遍历目录并过滤后缀
os.walk() 是最常用也最稳妥的方式,它天然按深度优先递归遍历,且不依赖外部包。关键不是“怎么递归”,而是“怎么精准匹配后缀”——很多人直接用 str.endswith('.py'),但遇到 script.py.bak 或大小写混用(如 README.MD)就漏掉或误判。
实操建议:
- 用
pathlib.Path(file).suffix.lower() == '.py'替代字符串结尾判断,避免多点扩展名干扰 - 把目标后缀统一转小写再比对,兼容 Windows/macOS 不区分大小写的习惯
- 注意
os.walk()返回的files是文件名列表,路径需拼接:os.path.join(root, file)
用 pathlib.Path.rglob() 更简洁地匹配
pathlib 在 Python 3.4+ 原生支持,rglob() 内置递归和通配,写法更接近直觉。但它底层仍调用系统级 glob 行为,某些 shell 特殊字符(如 [abc])在路径中可能意外匹配,且不支持正则。
实操建议:
- 匹配单后缀:用
Path('root').rglob('*.py')—— 注意是'*.py',不是'*.py'加斜杠 - 匹配多个后缀:不能直接
rglob('*.py|*.md'),得用两次再合并,或改用is_file() and p.suffix.lower() in {'.py', '.md'} - 如果目录极深或文件极多,
rglob()可能比os.walk()稍慢,因它会构造大量Path对象
避免 os.listdir() + 手动递归的坑
有人图省事自己写递归调用 os.listdir(),结果卡死或报错。这不是算法问题,而是没处理好三类边界:
- 符号链接循环:某个目录软链回祖先目录,
os.listdir()不检测,直接无限递归 - 权限拒绝:遇到
/proc/123/fd这类无读权限子目录,OSError不捕获就中断整个遍历 - 编码异常:路径含非 UTF-8 字节(尤其老 Linux 系统),
os.listdir()返回bytes,直接 decode 可能崩
除非你明确需要控制每一层行为,否则别手写递归 —— os.walk() 和 rglob() 已内置这些防护。
Windows 下注意 \ 和驱动器号处理
在 Windows 上,Path('C:\foo').rglob('*.log') 没问题,但若起始路径是相对路径(如 '.'),且当前在 D: 盘,rglob() 仍从 C: 开始?不会,它基于当前工作目录。真正容易错的是硬编码反斜杠:
- 写
'C:data*.txt'会因 被解释成制表符而报错 —— 必须用原始字符串r'C:data*.txt'或正斜杠'C:/data/*.txt' -
os.walk()返回的root在 Windows 是'C:\foo\bar'形式,拼接时用os.path.join(root, f)最安全,别手动加'\' - 跨平台脚本里,永远用
pathlib.Path处理路径,它自动适配分隔符和驱动器语法
递归遍历本身不难,难的是路径对象生命周期、编码上下文、符号链接策略这三件事——它们不出问题时没人注意,一出就是 FileNotFoundError 或无限循环,而且很难复现。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











