pathlib 如何递归遍历目录但排除特定子目录

12次阅读

正确做法是用 any(part == “node_modules” for part in path.parts) 判断路径是否含排除目录,或用 Python 3.12+ 的 Path.walk() 原地修改 dirs[:] 实现高效跳过;旧版本可封装 safe_rglob 函数配合 set 排除并处理符号链接与权限异常。

pathlib 如何递归遍历目录但排除特定子目录

pathlib.Path.rglob 配合条件过滤跳过指定子目录

rglob 本身不支持排除路径,必须手动过滤。常见错误是试图用 ** glob 模式直接“否定”某个目录名——这在 pathlib 中不可行,模式只支持匹配,不支持排除。

正确做法是遍历全部,再用 parentparts 判断路径是否落在要排除的目录下:

  • 检查 path.parent.name == "node_modules" 只能跳过一级子目录,漏掉 src/node_modules/utils 这类嵌套路径
  • 更可靠的是用 path.parts 遍历每一级目录名,或用 str(path).startswith() 做前缀判断(注意跨平台路径分隔符)
  • 推荐用 any(part == "node_modules" for part in path.parts),简洁且覆盖所有层级

pathlib.Path.walk(Python 3.12+)精准控制遍历行为

Python 3.12 引入的 walk 方法允许在进入子目录前修改 dirs 列表,实现真正意义上的“跳过”,避免无谓遍历:

for root, dirs, files in Path("project").walk():     # 在这里修改 dirs 列表,原地移除要跳过的目录名     dirs[:] = [d for d in dirs if d != "venv" and d != "__pycache__"]     for file in files:         print(root / file)

注意:dirs[:] 是关键,必须原地修改;直接赋值 dirs = [……] 无效。该方法比 rglob + 过滤更高效,尤其面对大量无关子目录时。

兼容旧版本 Python 的安全排除写法

若仍在用 Python os.walk 模拟相同逻辑,或封装一个带排除的 rglob 辅助函数:

def safe_rglob(root: Path, pattern: str = "*", exclude_dirs: set = None):     if exclude_dirs is None:         exclude_dirs = {"node_modules", ".git", "dist"}     for p in root.rglob(pattern):         if not any(part in exclude_dirs for part in p.parts):             yield p 

使用

for pyfile in safe_rglob(Path("."), "*.py"): print(pyfile)

这个函数的关键点在于:排除判断放在 yield 前,且作用于每个 p 而非仅顶层;exclude_dirsset 保证 O(1) 查找;p.parts 自动处理不同系统的路径分隔符。

容易被忽略的符号链接与权限问题

默认情况下,rglobwalk 都会跟随符号链接,可能意外进入不该访问的路径,甚至触发循环引用。如果目标目录含软链,务必加判断:

  • p.is_symlink() 提前跳过,或
  • walk 中检查 root / d 是否为符号链接再决定是否保留到 dirs
  • 遇到 PermissionError(如 /proc 下某些目录),需用 try/except 包裹,否则遍历中断

实际项目中,排除逻辑常和权限、符号链接、挂载点混在一起,光靠目录名过滤远远不够。

text=ZqhQzanResources