登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  python教程

Python pathlib.rglob递归查找文件时为什么漏掉隐藏目录怎样在批处理代码中减少额外分配

来源:17golang原创

时间:2026-11-10 05:14:24 161浏览 收藏

如果你用 Path.rglob() 扫描项目目录,发现结果里没有某个以点号开头的目录,先不要把原因归结为“pathlib 会忽略隐藏目录”。Python 官方文档明确说明,pathlib 中以点号开头的文件名并不特殊;更常见的原因是权限错误被扫描器抑制、目录其实是符号链接,或者后续代码把结果转成列表并做了额外过滤。

官方地址:https://docs.python.org/3/library/pathlib.html

这篇只解决一个批处理场景:递归找文件时怎样确认“漏掉”的真实边界,并让大量结果按需流过处理函数,减少一次性分配。

先确认:pathlib 不会自动排除点号目录

rglob(pattern) 相当于在递归 glob 的前面补上 **/。对于 Path 和 PurePath,点号并没有像传统 shell 通配规则那样获得特殊待遇,所以 root.rglob("*.json") 的匹配范围可以包含 .cache/data.json。返回顺序没有保证,需要顺序时再显式排序。

pathlib rglob 的匹配边界、隐藏目录、权限扫描和符号链接关系说明图
图1:结构说明图,展示 rglob 的模式范围与点号目录、权限边界、符号链接之间的关系;这是静态说明图,不是运行截图。

因此,下面这种写法通常没有必要:

from pathlib import Path

root = Path("data")

# pathlib 不把点号目录当成特殊目录,先按文件名模式递归查找
for path in root.rglob("*.json"):
    # 只保留普通文件,避免把目录匹配结果交给后续解析器
    if path.is_file():
        print(path)

如果目标是“包含隐藏目录”,不需要额外拼接一个 .* 分支。反过来,如果你使用的是 glob.glob(),再把它的隐藏文件规则直接套到 pathlib 上,才容易产生错误判断。

结果缺失时,优先排查三个真实边界

第一类是权限。官方文档说明,扫描目录时出现的 OSError(包括部分 PermissionError)可能被 glob() 和 rglob() 抑制,调用方只会看到“少了一些路径”。第二类是符号链接:递归展开 ** 时默认不跟随符号链接目录,避免意外进入环路。第三类是模式范围,例如只匹配 *.json 时,扩展名不符合的文件当然不会出现。

可以按这个顺序判断:先确认目标目录是否可读,再确认它是不是指向其他位置的链接,最后把实际传入的 pattern 和后续过滤条件单独记录。不要先把 rglob() 的结果用 list() 固化,因为那会同时掩盖扫描耗时和内存增长。

批处理不要先 list:让 Path 对象按需流过循环

rglob() 返回可迭代结果。批处理通常只需要“找到一个就处理一个”,不需要把所有路径同时留在内存中:

from pathlib import Path
from collections.abc import Iterator

def iter_json_files(root: Path) -> Iterator[Path]:
    # 直接返回迭代器,避免一次性创建完整路径列表
    for path in root.rglob("*.json"):
        # is_file 让目录名不会进入文件解析阶段
        if path.is_file():
            yield path

def consume(root: Path) -> None:
    # 消费一个处理一个,处理完后不保留全量 Path 引用
    for path in iter_json_files(root):
        text = path.read_text(encoding="utf-8")
        # 这里放单个文件的解析、入库或发送逻辑
        handle_record(path.name, text)

这个写法减少的是“结果集合”的额外分配,不代表磁盘扫描本身没有成本。不要在循环里反复调用 str(path)、resolve() 或把路径复制到多个中间列表;只有日志、序列化或接口参数确实需要字符串时再转换。若业务只关心后缀,尽量把条件放在 glob 模式中,而不是先匹配所有路径再二次筛选。

需要剪枝和看见错误时,用 Path.walk

当目录树很大,或者你必须跳过 .git、缓存目录,同时又想知道哪一级扫描失败,Path.walk() 更适合表达这个边界。它会把当前目录、子目录名和文件名分开交给调用方;在自顶向下遍历时,可以原地修改目录名列表来剪枝。

Path.walk 剪枝、错误回调与批处理消费者减少额外分配的关系说明图
图2:结构说明图,展示目录遍历、剪枝策略、错误回调与按需消费者的静态关系;这是结果示意图,不是运行截图。
from pathlib import Path

def report_scan_error(error: OSError) -> None:
    # 保留目录名和错误类型,方便区分权限问题与模式问题
    print(f"扫描失败: {error.filename} ({type(error).__name__})")

def iter_json_files(root: Path):
    for directory, dirnames, filenames in root.walk(on_error=report_scan_error):
        # 只剪掉明确不需要的目录;点号目录不会被默认删除
        dirnames[:] = [name for name in dirnames if name not in {".git", "__pycache__"}]
        for name in filenames:
            # 先按文件名筛选,再构造最终 Path,减少无关对象参与后续处理
            if name.endswith(".json"):
                yield directory / name

如果隐藏目录本身就是业务数据,不要写成 if name.startswith(".") 的统一过滤。若必须跟随符号链接,要明确设置相应选项并防范链接回指父目录的无限递归;如果只是普通递归查找,保留默认不跟随链接的行为更容易控制范围。

一张表记住排查与优化边界

现象优先判断处理方式
点号目录中的匹配文件不见了后续过滤或权限异常检查过滤条件,必要时用 Path.walk(on_error=...)
链接目录没有继续深入默认不递归符号链接确认是否真的需要跟随,并防止环路
内存随文件数增长调用了 list(rglob(...))直接迭代生成器,避免全量保存

结论很简单:rglob() 本身不是“隐藏目录过滤器”。先区分点号命名、权限、符号链接和模式范围,再决定继续用 rglob(),还是换成能剪枝并显式接收错误的 Path.walk()。批处理侧则坚持按需消费,通常比盲目优化字符串转换更值得优先处理。

相关问题

为什么要对 rglob 结果排序?因为官方文档不保证返回顺序;只有下游需要稳定顺序时才排序,否则会额外创建排序后的集合。

什么时候应该用 walk 而不是 rglob?需要剪枝、记录扫描错误、区分目录和文件名时用 walk() 更直观;只需匹配一个模式并按需消费时,rglob() 更简洁。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>