跳到正文
效率工具实用笔记关于本站
文件整理

用glob寻找文件:递归搜索与隐藏文件注意事项

约 8 分钟阅读AI 辅助整理

在自建练习目录里用glob查找文件时,递归要靠recursive=True配合**,隐藏文件默认不匹配、需用点号开头的模式或include_hidden,结果顺序不固定,需要自己排序。

本文要点

  1. 如果你要在一个目录及其子目录里找文件,只写普通通配符不够,需要用到**并开启递归

  2. 隐藏文件是glob里最容易踩空的一处

  3. 递归搜索还有一个需要提前知道的点:结果里可能出现重复路径名

用glob寻找文件:递归搜索与隐藏文件注意事项的知识要点示意图
依据已审核正文摘取要点制作的示意图,非实物照片;请结合正文中的条件与范围阅读。要点摘自已审核正文;完整条件与说明见正文。查看高清图解 ↗
本文目录

先明确:glob返回的是路径名列表,不是按顺序排好的结果

如果你在练习目录里用glob找文件,先接受一个前提:它返回的是一批路径名,顺序不固定。

按Python官方glob文档的说明,glob.glob(pathname)返回一个可能为空的路径名列表,这些路径名按Unix shell风格的通配规则匹配。文档专门提示:返回的路径名没有特定顺序;如果你需要特定顺序,要自己对结果排序。文档还补充,结果是否已排序取决于文件系统。

这句话决定了你的操作方式:拿到结果后不要假设第一个就是“最新的”或“最靠前的”,需要顺序就自己排。

本文只在你自己的练习目录里查找,例如exercise/下面。不要跟随未知外部路径,也不要把glob指向系统目录、其他程序目录或你不确定归属的位置。下面所有路径和文件都是自建练习示例,不是对真实目录的记录。动手前建议先复制一份练习目录,不要拿重要数据直接试。

一个边界:glob只做路径名匹配,不打开文件、不读内容、不判断文件是否有效。它返回的列表里可能包含失效的符号链接,文档说明broken symlinks会被包含在结果里。

递归搜索要显式开启:recursive=True和**必须一起用

如果你要在一个目录及其子目录里找文件,只写普通通配符不够,需要用到**并开启递归。

按文档说明,glob.glob(pathname, *, recursive=False)中,如果recursive为真,模式中的“**”会匹配任意文件和零个或多个目录、子目录以及指向目录的符号链接。也就是说,**负责“往下走”,recursive=True负责“允许它往下走”,两者缺一不可。

文档还给了一个容易忽略的细节:如果**后面跟着路径分隔符(os.sep或os.altsep),那么文件不会被匹配。换句话说,模式以**加斜杠结尾时,匹配到的是目录,不是文件。

下面是一个只针对练习目录的自建示例。假设练习目录exercise/demo_glob/下有若干自建文件,你想找出所有.txt:

import glob

pattern = "exercise/demo_glob/**/*.txt"
results = glob.glob(pattern, recursive=True)
for path in sorted(results):
    print(path)

这里用sorted包一层,是因为glob本身不保证顺序。sorted只是把返回的列表重新排列,不改变匹配到的内容。

本文只讲recursive和**这两个递归要素,不涉及root_dir、dir_fd等参数。

隐藏文件默认不匹配:点号开头的模式或include_hidden

隐藏文件是glob里最容易踩空的一处。

按文档说明,默认情况下,以点(.)开头的文件只能被同样以点开头的模式匹配。这一点和fnmatch.fnmatch()、pathlib.Path.glob()不同,文档专门做了对比提示。

文档给了一个例子:目录里有card.gif和.card.gif,用*.gif只会匹配到card.gif;要用.c*这样的模式,才会匹配到.card.gif。

所以如果你在练习目录里放了一个自建的隐藏文件,比如exercise/demo_glob/.hidden_notes.txt,那么:

import glob

print(glob.glob("exercise/demo_glob/*.txt"))
print(glob.glob("exercise/demo_glob/.*"))

第一行不会列出.hidden_notes.txt,第二行才会。第二行的模式以点开头,符合“点开头的文件只能被点开头的模式匹配”这条规则。

另一个办法是使用include_hidden参数。文档说明,glob.glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, include_hidden=False)中,如果include_hidden为真,通配符可以匹配以点开头的路径段。这个参数是Python 3.11新增的。

本文只讲隐藏文件默认不匹配这一条,以及点开头模式和include_hidden两种应对方式,不展开其他参数组合。

结果可能重复:多个**加递归时要自己去重

递归搜索还有一个需要提前知道的点:结果里可能出现重复路径名。

按文档说明,如果pathname里包含多个“**”模式,并且recursive为真,glob.glob()和glob.iglob()都可能返回重复的路径名。

这意味着,当你写了一个带多个**的模式时,不能默认每个文件只出现一次。需要唯一结果时,可以自己处理,例如把结果放进集合再排序:

import glob

pattern = "exercise/demo_glob/**/**/*.txt"
results = glob.glob(pattern, recursive=True)
unique_sorted = sorted(set(results))
for path in unique_sorted:
    print(path)

set负责去掉重复项,sorted负责给出稳定顺序。这两步都是在你拿到结果之后做的,不改变glob本身的匹配行为。

本文只讲重复结果这一种情况,不涉及其他去重场景。

权限错误会被吞掉:结果为空不等于目录里没有文件

用glob递归扫描时,如果某个子目录没有读取权限,结果可能悄悄少掉一部分,而不是报错。

按文档说明,扫描文件系统时抛出的任何OSError异常都会被抑制,其中就包括访问无读取权限目录时的PermissionError。

这条说明对你的判断很重要:当glob返回一个空列表或明显偏少的结果时,不能直接下结论说“目录里没有符合条件的文件”。可能的原因包括:模式写错、递归没开、隐藏文件没匹配、以及某些子目录因权限被跳过。

在自建练习目录里,你可以这样核对:先用一个更宽的模式看看到底能匹配到什么,再逐步收窄。

import glob

print(sorted(glob.glob("exercise/demo_glob/*")))
print(sorted(glob.glob("exercise/demo_glob/**/*", recursive=True)))

第一行只看当前层,第二行看递归结果。两行对比,能帮你判断差异是来自递归还是来自模式本身。

本文只讲权限错误被抑制这一条,不涉及如何修改权限。

大目录树和扫描期间的变化:两个使用限制

递归搜索有两个限制需要在使用前知道。

第一,文档提示,在大目录树上使用“**”模式可能消耗过多时间。这不是说不能用,而是说在层级很深、文件很多的目录上,递归匹配会比较慢。练习时建议先用小目录试,确认模式写对了再扩大范围。

第二,文档说明,如果在glob调用期间有满足条件的文件被删除或新增,这个文件是否出现在结果里是不确定的。也就是说,glob给出的是调用那一刻附近的一个快照,不是实时同步的视图。

这两条限制决定了glob适合什么场景:适合在相对稳定的自建练习目录里做一次性的路径名匹配,不适合在文件频繁变动的目录上依赖它给出精确的实时清单。

本文只讲这两个限制,不涉及性能优化或并发处理。

观看知识要点视频 18 秒

18秒无旁白要点导览,文字来自已审核正文;完整步骤、条件及参考资料见下方。

参考资料

以下原始资料提供本文依据;请结合日期、适用条件和产品说明查阅。

请结合原始资料的适用条件阅读。

返回文件整理 →