Skip to content

glob 文件名匹配 ​

当程序需要在指定目录中找到一批文件时,如何限制搜索范围、控制匹配规则,并确认结果不会误伤其他内容?glob 模块使用通配符批量匹配文件和目录名称,适合解决下面这类问题:

  • 找出一个目录中的所有 Python 文件
  • 递归查找所有 Markdown 文档
  • 匹配具有相似名称的一组文件

glob 只负责查找路径,不读取文件内容,也不会自动删除或移动文件。

glob 的搜索范围与匹配规则

文件名匹配规则 ​

通配符和正则表达式都能描述匹配规则,但它们不是同一种语法:

工具主要匹配对象示例
glob文件和目录名称*.py
正则表达式普通字符串内容r"[A-Za-z_]+\.py"

glob 中的 * 不是正则中的 .*,也不能在 glob 规则中使用 \d、+、() 等正则语法。

常用通配符:

通配符含义
*匹配一个路径片段中的任意数量字符
?匹配一个路径片段中的任意单个字符
[abc]匹配集合中的一个字符
[a-z]匹配范围中的一个字符
[!abc]匹配不在集合中的一个字符
**在递归模式下跨越多层目录

例如:

规则可能匹配的名称
*.pymain.py、student.py
test_?.pytest_a.py、test_1.py
[ab]*.logapp.log、backup.log
[!a]*.txt不以 a 开头的 .txt 文件

普通的 * 和 ? 不会跨越路径分隔符。需要进入多层目录时,应使用递归规则 **。

通配符语法对照与路径分隔符穿透机制

搜索范围与结果管理 ​

glob.glob(pattern) 返回所有匹配路径组成的列表:

python
import glob

python_files = glob.glob("*.py")
print(python_files)

相对规则默认从当前工作目录开始匹配,因此程序从不同目录启动时,结果可能不同。

文件系统没有保证返回顺序。需要稳定展示或处理时,应主动排序:

python
python_files = sorted(glob.glob("*.py"))

匹配结果可能包含文件,也可能包含目录。后续操作要求特定类型时,需要配合 Path.is_file() 或 Path.is_dir() 检查。

固定搜索范围:

Python 3.14 可以通过 root_dir 指定搜索根目录,不需要调用 os.chdir():

python
from pathlib import Path
import glob

script_dir = Path(__file__).resolve().parent
sample_dir = script_dir / "sample_files"

python_files = sorted(glob.glob("*.py", root_dir=sample_dir))
print(python_files)

规则是相对路径时,返回结果也相对于 root_dir。搜索过程不会改变程序的当前工作目录。

递归查找:

** 配合 recursive=True 可以进入多层目录:

python
markdown_files = sorted(
    glob.glob("**/*.md", root_dir=sample_dir, recursive=True)
)

print(markdown_files)

**/*.md 会匹配搜索根目录及其子目录中的 Markdown 文件。

递归扫描大型目录可能比较慢。应该把 root_dir 限制在明确范围内,不要轻易从用户主目录或磁盘根目录开始递归。

延迟读取结果:iglob()

glob.glob() 会先收集全部结果并返回列表。glob.iglob() 返回迭代器,可以逐个取得结果:

python
for path in glob.iglob("**/*.py", root_dir=sample_dir, recursive=True):
    print(path)

结果较多,或者只需要逐项处理时,可以使用 iglob()。如果展示顺序必须稳定,仍然需要收集结果后排序。

特殊名称与隐藏内容 ​

glob 默认不会让普通通配符匹配以点开头的隐藏名称。例如,*.py 不会匹配 .hidden.py,递归查找也不会自动进入 .hidden 目录。

如果明确需要包含隐藏内容,可以设置 include_hidden=True:

python
all_python_files = sorted(
    glob.glob(
        "**/*.py",
        root_dir=sample_dir,
        recursive=True,
        include_hidden=True,
    )
)

也可以在规则中明确写出点号,例如 .hidden/*.py。是否处理隐藏文件应该是主动选择,而不是无意扩大扫描范围。

按字面匹配特殊文件名:

文件名本身可能包含 *、? 或 [。如果希望按字面匹配这些字符,可以使用 glob.escape():

python
special_name = "[draft].txt"
escaped_pattern = glob.escape(special_name)

result = glob.glob(escaped_pattern, root_dir=sample_dir)
print(result)

如果直接把 [draft].txt 当作规则,方括号会被解释为字符集合,不会表示完整的字面文件名。

隐藏文件过滤机制与特殊符号转义避坑

与 pathlib 配合 ​

glob 模块返回字符串路径:

python
paths = glob.glob("**/*.py", root_dir=sample_dir, recursive=True)

如果已经使用 Path 管理路径,也可以直接调用:

python
python_files = sorted(sample_dir.glob("*.py"))
all_python_files = sorted(sample_dir.rglob("*.py"))
写法返回结果搜索范围
glob.glob()字符串列表由规则和 root_dir 决定
glob.iglob()字符串迭代器由规则和 root_dir 决定
Path.glob()Path 对象迭代器当前路径下按规则匹配
Path.rglob()Path 对象迭代器当前路径下递归匹配

隐藏文件行为也有区别:glob 默认忽略点号开头的名称,而 Path.glob()、Path.rglob() 会把点号当作普通字符。

glob 模块与 pathlib.Path.glob 全维特性与选型对比

案例:查找课程文件 ​

需求: 在固定的 sample_files 目录中查找 Markdown 文件、可见与隐藏的 Python 文件,以及名称包含特殊字符的文本文件,并只输出匹配路径。

功能分析: 使用 root_dir 限制搜索范围,用 ** 和 recursive=True 查找子目录,分别用默认规则与 include_hidden=True 对比隐藏文件,再用 iglob() 和 glob.escape() 处理不同结果形式与特殊文件名。

准备下面的示例目录:

text
01-find-course-files/
├── main.py
└── sample_files/
    ├── .hidden/
    │   └── secret.py
    ├── docs/
    │   └── intro.md
    ├── tests/
    │   └── test_app.py
    ├── [draft].txt
    ├── README.md
    └── app.py

按目录树创建样本文件。隐藏目录 .hidden 和文件名 [draft].txt 要保持原样,才能观察两种匹配规则的差异。

text
# 示例课程

用于 glob 文件匹配练习。
text
# 课程说明

glob 可以按照通配符查找文件。
text
文件名中包含方括号。

样本中的三个 Python 文件各写入一行,程序只匹配它们的路径,不执行其内容:

python
print("示例程序")
python
print("示例测试文件")
python
print("隐藏目录中的示例")

程序固定搜索 sample_files,分别查找文档、可见 Python 文件、全部 Python 文件和特殊文件名:

python
from pathlib import Path
import glob


def show_paths(title, paths):
    print(f"{title}:")
    for path in paths:
        print(f"- {path}")


def main():
    script_dir = Path(__file__).resolve().parent
    sample_dir = script_dir / "sample_files"

    markdown_files = sorted(
        glob.glob("**/*.md", root_dir=sample_dir, recursive=True)
    )
    visible_python_files = sorted(
        glob.iglob("**/*.py", root_dir=sample_dir, recursive=True)
    )
    all_python_files = sorted(
        glob.glob(
            "**/*.py",
            root_dir=sample_dir,
            recursive=True,
            include_hidden=True,
        )
    )
    hidden_python_files = sorted(
        set(all_python_files) - set(visible_python_files)
    )
    special_files = glob.glob(
        glob.escape("[draft].txt"),
        root_dir=sample_dir,
    )

    show_paths("Markdown 文件", markdown_files)
    show_paths("可见 Python 文件", visible_python_files)
    show_paths("额外发现的隐藏 Python 文件", hidden_python_files)
    show_paths("特殊文件名", special_files)


if __name__ == "__main__":
    main()

运行结果:

text
Markdown 文件:
- README.md
- docs/intro.md
可见 Python 文件:
- app.py
- tests/test_app.py
额外发现的隐藏 Python 文件:
- .hidden/secret.py
特殊文件名:
- [draft].txt

案例只查找并输出路径,不会读取、修改或删除文件。

批量操作安全边界 ​

glob 本身是只读查找,但匹配结果经常会继续交给复制、移动或删除操作。批量修改之前应确认:

  1. root_dir 是否限制在预期目录。
  2. 通配符是否匹配了额外文件或目录。
  3. 是否需要包含隐藏文件。
  4. 结果顺序是否会影响后续处理。
  5. 是否已经先输出并人工检查匹配列表。

不要把未经检查的宽泛规则直接交给 unlink()、move() 或 rmtree()。

通配检索交付批处理的 4 道安全防御门禁

小结 ​

  • glob 使用文件名通配符匹配路径,不使用正则表达式语法。
  • glob() 返回列表,iglob() 返回可以逐个读取的迭代器。
  • root_dir 可以固定搜索范围,而不改变当前工作目录。
  • ** 配合 recursive=True 可以递归进入子目录。
  • glob 默认忽略隐藏名称,可以通过 include_hidden=True 主动包含。
  • glob.escape() 可以按字面匹配包含特殊字符的文件名。
  • Path.glob() 和 Path.rglob() 返回 Path 对象,并且隐藏文件行为不同。

练习 ​

  1. 使用 root_dir 查找示例目录中的所有 .txt 文件。
  2. 使用 ? 匹配名称中只有一个可变字符的文件。
  3. 分别使用 glob() 和 iglob() 递归查找 Python 文件。
  4. 比较默认模式和 include_hidden=True 的结果。
  5. 使用 glob.escape() 匹配 [draft].txt。
  6. 使用 Path.rglob() 重写综合案例中的 Markdown 文件查找。