Watch at Ease看见世界,读懂生活。

科技 · 实用指南

Pathlib 总能找到隐藏文件?应剪枝遍历,而不是事后过滤

结果过滤发生在 Python 已进入目录之后。先定义“隐藏”的规则,自顶向下剪枝,再核对真正没有被访问的目录树。

Original diagram comparing a pathlib result filter that still enters a hidden subtree with top-down pruning that prevents descent.
原创示意图:事后过滤仍进入隐藏子树,自顶向下剪枝则不会。 Watch at Ease

Pathlib 不把点号开头名称当成通用排除规则;它的 glob 可以匹配点文件,而 glob.glob 另有 include_hidden 参数。更重要的是,rglob 返回路径后再丢弃,只是隐藏结果,并不会阻止 Python 进入该目录并枚举后代。

扫描前先写清策略:隐藏是 POSIX 点号前缀、Windows 隐藏属性、应用缓存名称,还是组合规则?随后使用自顶向下遍历,在进入前从 dirnames 移除目录,单独过滤文件,并保存已访问、已跳过、符号链接和错误台账。

先写清隐藏文件合同

记录根目录、平台、Python 版本与扫描目的。明确点号规则作用于文件、目录还是两者,Windows 隐藏属性是否计入,以及 build、vendor 等名称是否另行排除。

除非真正检查各平台元数据,不要宣称单一规则“跨平台”。可以共享应用规则,但平台判断必须保持显式。

进入前剪除目录

Python 3.12+ 使用 root.walk(top_down=True)。逐个构造子 Path,保留允许进入的名称,并用切片赋值写回 dirnames;被移除的目录不会被遍历。

旧版 Python 可用 os.walk(topdown=True) 同样处理。仅重新绑定本地变量无效,必须修改 walker 提供的列表。

分别过滤文件并解释 glob 差异

目录剪枝后,再用同一策略检查文件名,处理 .DS_Store 等叶文件。不要把文件过滤与子树剪枝混为一谈。

如果使用 glob.glob,要显式记录 include_hidden。两个 API 都支持 * 和 **,并不代表隐藏项默认值相同;大型目录上的递归 ** 也可能昂贵。

明确处理 Windows 属性与符号链接

Windows 上要用受支持的元数据调用检查目录和文件的 FILE_ATTRIBUTE_HIDDEN。点号规则仅在应用需要时保留,两者不是同一信号。

默认不跟随目录符号链接。若业务必须跟随,记录稳定目录身份并拒绝重复,避免链接或 junction 形成无界循环。

用验收清单记录错误和跳过

用 on_error 或等效边界记录路径、操作与异常。明确权限错误是使扫描失败、标记子树不完整,还是允许跳过;绝不能静默抹除。

验收清单应包含根目录、策略版本、访问目录数、输出文件数、带原因的跳过根、链接策略和错误。用含点目录、普通目录、链接及拒绝访问目录的小型 fixture 做回归测试。

Original decision diagram separating dot-prefixed names, Windows hidden attributes, and application-specific exclusions.
原创示意图:区分点号名称、Windows 隐藏属性与应用排除规则。 Watch at Ease