glob模块 — Unix风格路径模式扩展

Python标准库精讲专题 · 文件与目录处理篇 · 掌握路径模式匹配

专题:Python标准库精讲系统学习

关键词:Python, 标准库, glob, 通配符, 文件匹配, 路径搜索, 递归查找, iglob, 文件过滤

一、glob模块概述

glob模块是Python标准库中用于Unix风格路径模式匹配的工具,它提供了一种简单而强大的方式在文件系统中查找符合特定模式的文件和目录。与正则表达式不同,glob使用通配符模式匹配,语法更简洁直观,特别适合日常的文件查找和过滤任务。

glob的名称来源于Unix系统中的glob命令(global command的缩写),后者最早出现在Unix的早期版本中,用于扩展Shell中的通配符。Python的glob模块继承了这一思想,使开发者能够在代码中方便地实现文件路径的模式匹配,而无需依赖Shell环境。

正则表达式擅长对文本内容进行复杂模式匹配,但用于文件路径查找时显得过于繁琐。glob通配符则专注于文件名和路径的模式匹配,语法精炼,可读性强。例如,查找所有Python文件只需要一个简单的模式 *.py,而用正则表达式则需要写出 ^.+\.py$。这使得glob成为文件批量操作的首选方案。

核心定位:glob是Python文件IO操作体系中面向用户的"快速查找层",它建立在os.listdir()和os.scandir()之上,提供更高层次的通配符匹配能力。对于简单的文件查找需求,glob比正则表达式更简洁、更不易出错。

二、通配符语法规则

glob模块支持的通配符与Unix Shell中的模式匹配规则基本一致,主要包括以下几种特殊字符,它们各自对应不同的匹配行为。

* 通配符(匹配任意多个字符)

* 是最常用的通配符,它可以匹配任意数量的字符(包括零个字符),但不匹配路径分隔符(在Windows上是 \,在Linux/macOS上是 /)。这意味着 * 只会在当前目录层级内进行匹配,不会跨越子目录。例如,*.txt 会匹配当前目录下所有扩展名为 .txt 的文件,如 readme.txtdata.txt 等,但不会匹配子目录中的文件。

? 通配符(匹配单个字符)

? 匹配任意单个字符,但同样不匹配路径分隔符。它在需要精确控制文件名长度的场景中非常有用。例如,file?.txt 可以匹配 file1.txtfileA.txtfile_.txt,但不匹配 file10.txt(因为 ? 只匹配一个字符)。

[abc] 字符集匹配

[] 用于定义一个字符集合,匹配方括号内列出的任意一个字符。例如,[abc].txt 会匹配 a.txtb.txtc.txt 这三个文件。还可以使用连字符 - 表示字符范围,如 [a-z].txt 匹配所有小写字母命名的txt文件,[0-9].csv 匹配所有单个数字命名的csv文件。

[!abc] 排除匹配

[!] 用于排除指定字符,与 [] 的行为相反。例如,[!abc].txt 匹配除 a.txtb.txtc.txt 之外的任何单个字符命名的txt文件。同样的,[!0-9].log 将匹配所有不以数字开头的log文件。这个语法在过滤不需要的文件时非常高效。

** 递归匹配(Python 3.5+)

** 是递归通配符,用于跨目录层级的匹配。当设置 recursive=True 参数时,** 可以匹配零个或多个目录层级。例如,**/*.py 会递归查找当前目录及所有子目录中的所有Python文件。这是实现"全项目搜索"的关键语法,大大简化了传统递归遍历的代码量。

通配符含义示例匹配结果
*匹配任意数量字符(不含分隔符)*.pymain.py, utils.py
?匹配单个字符data_?.csvdata_1.csv, data_a.csv
[abc]匹配指定字符之一[12].txt1.txt, 2.txt
[!abc]排除指定字符[!12].txt3.txt, a.txt(排除1.txt和2.txt)
**递归匹配零个或多个目录**/*.loga.log, sub/b.log, sub/sub/c.log

三、glob() 与 iglob() 核心函数

glob模块提供了两个核心函数:glob.glob()glob.iglob()。它们的功能相似,但返回值和性能特性有显著差异,开发者应根据实际场景选择使用。

glob.glob() — 返回列表

glob.glob(pathname, *, recursive=False, include_hidden=False) 接受一个模式字符串作为参数,返回所有匹配路径组成的列表。返回的路径顺序与文件系统本身的顺序一致,通常按字母序排列。由于列表会一次性将所有匹配结果加载到内存中,适合匹配结果数量较少(成千上万级别)的场景。

import glob # 查找当前目录下所有Python文件 py_files = glob.glob("*.py") print(py_files) # ['main.py', 'utils.py', 'config.py'] # 递归查找所有子目录中的图片文件 images = glob.glob("**/*.png", recursive=True) print(images) # ['logo.png', 'assets/icon.png', 'assets/bg/banner.png'] # 使用字符集匹配多个扩展名 data_files = glob.glob("data.[ct]sv") print(data_files) # ['data.csv', 'data.tsv']

glob.iglob() — 返回迭代器

glob.iglob(pathname, *, recursive=False, include_hidden=False) 返回一个迭代器,按需逐个生成匹配结果,而非一次性加载所有路径。这在处理海量文件时具有显著的内存优势。例如,当要遍历上百万个日志文件时,iglob() 可以边遍历边处理,而 glob() 会先构建一个巨大的列表,消耗大量内存。

import glob # 使用 iglob 逐个处理文件,节省内存 for log_file in glob.iglob("logs/**/*.log", recursive=True): process_log(log_file) # 逐个处理,不会一次性加载所有路径 # 也可以直接转为列表(但不推荐) all_files = list(glob.iglob("*")) # 等同于 glob.glob("*")

recursive参数

recursive 参数自Python 3.5引入,当设置为 True 时,模式中的 ** 将匹配任意深度的子目录。若设置为 False(默认值),则 ** 仅被视为两个连续的 *,只匹配当前目录层级。需要注意的是,启用递归匹配会遍历整个目录树,在深层嵌套的大型文件系统中可能会有性能开销。

include_hidden参数(Python 3.11+)

include_hidden 是Python 3.11版本新增的参数。当设置为 True 时,glob将匹配以点号 . 开头的隐藏文件和目录。默认值为 False,即glob会忽略隐藏文件。这一参数对于需要在类Unix系统中处理隐藏配置文件的场景非常重要。

import glob # Python 3.11+ 支持 include_hidden 参数 # 查找所有隐藏文件 hidden = glob.glob(".*", include_hidden=True) print(hidden) # ['.gitignore', '.env', '.config'] # 递归查找隐藏目录中的文件 config_files = glob.glob("**/*.conf", recursive=True, include_hidden=True)

四、pathlib的glob方法

Python 3.4引入的pathlib模块提供了面向对象的文件系统路径操作方式,其中也包含了glob方法的等效实现。在现代化Python代码中,pathlib的glob方法正在逐渐取代传统的glob模块函数。

Path.glob() 与 Path.rglob()

Path.glob(pattern) 是pathlib中与 glob.glob() 对应的接口。它返回一个生成器,生成当前路径下匹配指定模式的 Path 对象。而 Path.rglob(pattern) 是递归版本的简写,等价于 Path.glob("**/" + pattern),用于递归搜索所有子目录。

from pathlib import Path # 使用 Path.glob() 查找文件 p = Path(".") for py_file in p.glob("*.py"): print(py_file) # 输出 Path 对象 # 递归查找所有日志文件 for log in Path("logs").rglob("*.log"): print(log.resolve()) # 输出绝对路径 # 链式调用 file_count = len(list(Path("src").rglob("*.py"))) print(f"Python源文件数量: {file_count}")

Path.match() 模式匹配

Path.match(pattern) 方法用于检查路径是否与指定的通配符模式匹配。这是一个非常有用的布尔判断工具,特别适合在遍历目录时筛选特定文件。注意 match() 是从路径的尾部开始匹配的,这意味着模式不需要匹配完整的路径前缀。

from pathlib import Path # 检查文件名是否匹配 path = Path("/home/user/docs/report.md") print(path.match("*.md")) # True print(path.match("*.txt")) # False print(path.match("docs/*.md")) # True(从尾部匹配) # 在遍历中结合 glob 和 match 使用 for f in Path("/var/log").rglob("*"): if f.match("access.log*") or f.match("error.log*"): print(f"找到日志文件: {f}")

glob模块 vs pathlib:如何选择

两者各有所长。传统 glob 模块的优势在于简洁直接的函数调用,适合快速脚本和一次性任务。而 pathlib 的glob方法更符合面向对象的设计理念,返回的 Path 对象可以直接调用 .read_text().resolve().stat() 等方法,代码更加优雅流畅。对于新项目,官方推荐优先使用pathlib的glob接口;但对于简单场景或需要兼容旧版Python时,传统glob模块仍然是可靠的选择。

Python官方文档建议:在新代码中优先使用 pathlib 的 Path.glob() 和 Path.rglob() 方法,它们提供了更一致的对象接口和更丰富的路径操作方法。

五、实战案例

案例一:批量查找日志文件

在运维和调试工作中,经常需要批量查找和处理日志文件。下面的示例演示了如何查找所有nginx访问日志,并按日期分类统计大小。

import glob import os # 递归查找所有 access.log 相关文件 log_pattern = "**/access.log*" total_size = 0 file_count = 0 for log_path in glob.glob(log_pattern, recursive=True): size = os.path.getsize(log_path) total_size += size file_count += 1 print(f"{log_path} -> {size / 1024:.1f} KB") print(f"共找到 {file_count} 个日志文件,总大小: {total_size / 1024:.1f} KB")

案例二:图片资源收集器

前端开发中经常需要从项目的各个目录中收集所有图片资源,统一检查或迁移。通过glob的字符集语法,可以一次性匹配多种常见图片格式。

import glob import os from pathlib import Path # 使用字符集匹配多种图片格式 image_exts = "*.{jpg,jpeg,png,gif,svg,webp}" image_dir = Path("./static/assets") # 递归收集所有图片文件 all_images = list(image_dir.rglob(image_exts)) # 按扩展名分组统计 ext_count = {} for img in all_images: ext = img.suffix.lower() ext_count[ext] = ext_count.get(ext, 0) + 1 print(f"共找到 {len(all_images)} 张图片:") for ext, count in sorted(ext_count.items()): total_size = sum( img.stat().st_size for img in all_images if img.suffix.lower() == ext ) print(f" {ext}: {count} 个文件, 共 {total_size / 1024:.1f} KB")

案例三:多类型文件批量处理

数据工程师经常需要在杂乱的数据目录中筛选出指定类型的文件进行处理。结合glob的排除语法和递归搜索,可以精确地圈定目标文件。

import glob # 查找所有数据文件,但排除临时文件和备份文件 target_files = [] for pattern in ["*.csv", "*.json", "*.xlsx"]: # 递归查找数据文件 for f in glob.glob(f"data/**/{pattern}", recursive=True): # 进一步过滤:排除以 ~ 开头的临时文件和 .bak 备份文件 basename = f.split("/")[-1] if not basename.startswith("~") and not basename.endswith(".bak"): target_files.append(f) print(f"待处理的数据文件: {len(target_files)} 个") for f in sorted(target_files): print(f" - {f}")

案例四:清理空目录

结合glob和os模块,可以编写一个清理空目录的小工具,帮助维护文件系统的整洁。

import glob import os def remove_empty_dirs(root_dir="."): """递归删除所有空目录""" # 先收集所有目录(从最深开始) all_dirs = sorted( glob.glob(f"{root_dir}/**/", recursive=True), key=lambda x: x.count(os.sep), reverse=True ) removed = 0 for d in all_dirs: if d == root_dir + "/": continue try: if not os.listdir(d): os.rmdir(d) print(f"删除空目录: {d}") removed += 1 except OSError: pass print(f"共清理 {removed} 个空目录") remove_empty_dirs("./project")

六、核心总结

1. glob模块的核心价值:提供Unix风格的通配符路径匹配,比正则表达式更简洁直观,是文件批量查找和过滤的最佳入门工具。

2. 五种通配符各有用途:* 匹配任意字符,? 匹配单个字符,[abc] 匹配字符集,[!abc] 排除字符,** 实现递归搜索。

3. glob() 与 iglob() 的选择:结果集小时用 glob() 直接获取列表;结果集大时用 iglob() 迭代处理以节省内存。

4. pathlib 是未来趋势:Path.glob()Path.rglob() 返回Path对象,支持链式调用,是新项目推荐的首选方案。

5. 注意兼容性:recursive 参数需要Python 3.5+,include_hidden 参数需要Python 3.11+,在老旧环境中需注意版本限制。

6. 实战场景丰富:从日志收集到图片整理,从数据筛选到目录清理,glob模块在各种文件处理任务中都能发挥重要作用。