68人参与 • 2026-10-11 • Python
在 python 中处理文件系统时,glob 模块是搜索文件的首选工具。它让你用简洁的通配符模式(如 *.txt、data/**/*.csv)轻松定位文件,不必手写复杂的目录遍历逻辑。然而,很多开发者在使用 glob 时都会遇到令人困惑的现象:明明存在的文件却搜索不到;在 windows 上工作正常的模式,到了 linux 上结果完全不同;递归匹配时返回了意料之外的嵌套目录;或者搜索速度随着目录层级增加而急剧下降。更隐蔽的是,glob 返回的顺序没有保证,依赖它做后续处理可能在不同运行中产生不一致的结果。
这些问题的根源,在于 glob 的匹配规则并非通用正则表达式,而是遵循 unix shell 的文件名扩展约定,且其行为深受操作系统、文件系统隐藏文件规则以及递归实现细节的影响。今天,我们就来彻底解剖 glob 模块的匹配机制,看透那些让你“找不到文件”或“搜错文件”的陷阱,并掌握从简单模式到复杂递归搜索的正确姿势。

import glob
import os
os.makedirs('test', exist_ok=true)
open('test/.hidden.txt', 'w').close()
open('test/visible.txt', 'w').close()
print(glob.glob('test/*.txt'))
# 输出:['test/visible.txt'],.hidden.txt 被忽略了
在 unix 系统中,以 . 开头的文件被视为隐藏文件,* 通配符默认不匹配它们。这是 shell 的传统行为,glob 也继承了这个规则。如果你需要匹配隐藏文件,必须显式使用 .* 模式,例如 test/.*.txt,但这同时会匹配 . 和 ..,需要额外过滤。
import glob
# 需要 recursive=true 才生效
files = glob.glob('**/*.py') # 在 python 3.5+ 中,不加 recursive=true 只匹配当前目录
很多开发者以为 ** 会自动递归,但忘记了 recursive=true 参数。在 python 3.5 之前,** 甚至不被支持。即使加了参数,** 匹配的深度也取决于实现。
import glob
# 在 windows 上,路径分隔符是反斜杠
files = glob.glob('c:\\data\\*.txt') # 可行,但容易因转义出错
files = glob.glob('c:/data/*.txt') # 在 windows 上通常也可以
# 在 linux 上
files = glob.glob('/data/*.txt')
如果你在代码中硬编码了平台特定的分隔符,跨平台时就会失败。更安全的方式是使用 os.path.join 或 pathlib 来构造模式。
import glob
import os
os.makedirs('a/b/c', exist_ok=true)
open('a/b/c/file.txt', 'w').close()
print(glob.glob('a/**', recursive=true))
# 输出包含 'a/', 'a/b/', 'a/b/c/', 'a/b/c/file.txt'
** 不仅匹配文件,还会匹配目录。如果你只想要文件,需要在结果中过滤 os.path.isfile()。
files = glob.glob('*.txt')
# 文件的返回顺序取决于文件系统的目录项顺序,而非字母顺序
在不同机器、不同文件系统、甚至同一机器的不同运行中,顺序可能不同。如果你的代码依赖于“第一个文件”或“按顺序处理”,就会产生不一致的结果。应显式排序。
import glob
# 文件名包含方括号
files = glob.glob('data[1].txt') # 方括号被解释为字符集,而不是字面量
如果要匹配字面意义上的 [ 或 ],需要使用 glob.escape() 或 glob.escape 函数。
import glob
import os
os.symlink('/target/dir', 'link_dir')
print(glob.glob('link_dir/*.txt')) # 取决于操作系统和实现,可能不进入符号链接
glob 默认是否跟随符号链接在递归模式下由 include_hidden 等参数影响,且平台行为不一致。
glob 支持四种通配符,与 unix shell 的规则一致:
| 通配符 | 含义 |
|---|---|
* | 匹配任意数量的字符(包括零个),但不匹配路径分隔符 /,也不匹配以 . 开头的文件(除非模式以 . 开头) |
? | 匹配任意单个字符,不匹配路径分隔符 |
[seq] | 匹配 seq 中的任意一个字符。支持范围如 [a-z],也支持 [!seq] 排除 |
** | 递归匹配任意层级的目录,仅在 recursive=true 时有效 |
在 unix 传统中,* 不匹配以 . 开头的文件。因此 glob('*') 会忽略 .bashrc、.git 等。若要匹配隐藏文件,必须显式以 . 开头,如 glob('.*')。但 .* 也会匹配 . 和 ..,通常需要过滤。
当使用 ** 且 recursive=true 时,glob 会遍历目录树。** 匹配任意数量的目录层级(包括零层),因此 **/*.py 会匹配当前目录及所有子目录中的 .py 文件。
glob 的匹配逻辑基于 fnmatch 模块,后者实现了 unix shell 风格的通配符匹配。但 glob 在 fnmatch 基础上增加了路径分隔符的处理和目录遍历。
glob.glob() 返回一个列表,一次性加载所有结果。glob.iglob() 返回一个迭代器,按需生成结果,内存效率更高,适合处理大量文件。pathlib.path 提供了面向对象的 glob 方法,语法更现代:
from pathlib import path
p = path('.')
for f in p.glob('**/*.py'):
print(f)
注意:pathlib 的 glob 在 python 3.5+ 中自动支持 **,不需要 recursive=true 参数。
glob 不保证返回顺序。它依赖于底层 os.scandir() 或 os.listdir() 的顺序,而后者取决于文件系统的目录项排列。因此,需要排序时请显式使用 sorted()。
glob.glob('**/*.py') # 只匹配当前目录的 .py 文件,不递归
glob.glob('**/*.py', recursive=true) # 正确,递归所有子目录
files = glob.glob('**/*', recursive=true)
# 包含了目录
files = [f for f in files if os.path.isfile(f)]
# 在 linux 上
glob.glob('data\\*.txt') # 不匹配,因为 \ 不是分隔符
应使用 / 或 os.sep,或使用 pathlib。
files = glob.glob('*.txt')
first = files[0] # 可能是任意文件
始终 sorted(glob.glob(...))。
glob.glob('file[1].txt') # 方括号被解释为字符集
glob.glob(glob.escape('file[1].txt')) # 正确匹配字面量
在某些情况下,符号链接指向的目录不会被递归进入。如果需要跟随,应使用 os.walk(followlinks=true) 或 pathlib 的 rglob 配合相应处理。
应使用 glob.iglob() 或 pathlib.path.rglob() 迭代处理。
在 linux 上,文件名匹配是大小写敏感的;在 windows 和 macos(默认)上,文件系统不区分大小写。因此 glob('*.txt') 在 linux 上可能匹配不到 file.txt,而在 windows 上可以。跨平台代码应统一大小写或使用 fnmatch 的 case_sensitive 参数(python 3.13+)。
在某些实现中,** 递归可能因为符号链接循环而无限递归。python 的 glob 会检测并跳过循环,但如果你自己实现递归,需要小心。
import glob
# 当前目录所有 .txt 文件
txt_files = glob.glob('*.txt')
# 指定目录
txt_files = glob.glob('/path/to/dir/*.txt')
# 匹配多种扩展名
files = glob.glob('*.txt') + glob.glob('*.md')
# 或使用字符集
files = glob.glob('*.[tm]*') # 匹配 .txt, .md 等,但可能过宽
# 所有 .py 文件(包括子目录)
py_files = glob.glob('**/*.py', recursive=true)
# 只匹配文件,排除目录
py_files = [f for f in glob.glob('**/*.py', recursive=true) if os.path.isfile(f)]
# 匹配所有文件,包括隐藏文件
all_files = glob.glob('*') + glob.glob('.*')
# 过滤掉 . 和 ..
all_files = [f for f in all_files if f not in ('.', '..')]
或者使用 pathlib 的 path.glob 并配合 include_hidden=true(python 3.11+):
from pathlib import path
files = list(path('.').glob('*', include_hidden=true))
import glob
pattern = glob.escape('file[1].txt') # 转义特殊字符
files = glob.glob(pattern)
files = sorted(glob.glob('*.txt'))
# 或按修改时间排序
files = sorted(glob.glob('*.txt'), key=os.path.getmtime, reverse=true)
from pathlib import path
# 递归搜索
for p in path('.').rglob('*.py'):
print(p)
# 匹配当前目录
for p in path('.').glob('*.txt'):
print(p)
# 包含隐藏文件(python 3.11+)
for p in path('.').glob('*', include_hidden=true):
print(p)
import glob
for filepath in glob.iglob('**/*.log', recursive=true):
process(filepath)
import os import glob base = '/data' # 或使用 path pattern = os.path.join(base, '*.txt') files = glob.glob(pattern)
使用 pathlib 更简洁:
from pathlib import path
base = path('/data')
files = list(base.glob('*.txt'))
import glob
import os
def case_insensitive_glob(pattern):
# 获取目录和文件名模式
dirname, basename = os.path.split(pattern)
if not dirname:
dirname = '.'
# 使用 fnmatch 手动过滤
import fnmatch
results = []
for entry in os.listdir(dirname):
if fnmatch.fnmatch(entry.lower(), basename.lower()):
results.append(os.path.join(dirname, entry))
return results
python 3.13+ 的 glob.glob 支持 case_sensitive 参数(部分平台),但兼容性有限。
glob 前打印 repr(pattern),确认转义和分隔符是否正确。os.listdir() 对比:查看目录下实际有哪些文件,确认是否被隐藏或过滤。os.walk() 交叉验证:确认递归搜索是否遗漏了某些目录。glob.escape:当文件名包含特殊字符时,确保已转义。glob、os.walk、pathlib 的性能。pathlib 的 glob/rglob 作为现代首选,代码更清晰,跨平台更好。** 递归时记得传 recursive=true(glob.glob/iglob),pathlib 则自动支持。* 匹配,需要显式处理。glob.escape() 转义文件名中的特殊字符。os.path.isfile()。iglob() 或迭代器,避免一次性加载。os.path.join 或 pathlib。os.walk(followlinks=true)。glob 模块是文件搜索的快捷方式,但它远非万能的魔法。它遵循着 unix shell 的古老规则,对隐藏文件、路径分隔符、递归深度和返回顺序都有自己的一套逻辑。理解这些规则,你就能写出精准、可移植的搜索代码,不再为“明明有文件却搜不到”而困惑。从今天起,当你面对复杂的文件搜索需求时,先想想 glob 是否合适,如果不合适,pathlib.rglob 或 os.walk 可能是更好的选择。掌握这些工具,你的文件系统操作将如虎添翼,无论面对多么混乱的目录结构,都能快速找到你需要的文件。
以上就是深度解析python中glob模块模式匹配的规则与常见坑的详细内容,更多关于python glob匹配规则的资料请关注代码网其它相关文章!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论