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。返回顺序没有保证,需要顺序时再显式排序。

因此,下面这种写法通常没有必要:
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() 更适合表达这个边界。它会把当前目录、子目录名和文件名分开交给调用方;在自顶向下遍历时,可以原地修改目录名列表来剪枝。

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() 更简洁。
-
346 收藏
-
235 收藏
-
225 收藏
-
389 收藏
-
250 收藏
-
494 收藏
-
118 收藏
-
476 收藏
-
167 收藏
-
195 收藏
-
127 收藏
-
474 收藏
-
128 收藏
-
337 收藏
-
116 收藏
-
文章 · python教程 | 2天前 | python · 版本升级 · sys.version_info Python 3.14.10 Python安全修订 ssl.OPENSSL_VERSION pip-audit167 收藏
-
423 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习