Skip to content
Python Basics: Matching Filename Patterns with fnmatch

Python Basics: Matching Filename Patterns with fnmatch

October 12, 2026

Imagine receiving a list of export filenames and needing only the daily sales reports. Python’s standard-library fnmatch module checks strings against shell-style patterns. It does not search your disk.

Use Python 3.12 or newer in Bash on Linux. Each example runs independently and creates no data files.

Filter a list of names

Save as demo_fnmatch_1.py:

import fnmatch

filenames = ["sales-01.csv", "notes.txt", "sales-02.csv", "sales-final.csv"]
daily_exports = fnmatch.filter(filenames, "sales-??.csv")
print(daily_exports)
print(fnmatch.fnmatch("notes.txt", "*.csv"))

Run it from the directory where you saved it:

python3 demo_fnmatch_1.py

Output:

['sales-01.csv', 'sales-02.csv']
False

? matches one character; * matches any number, including zero. filter() returns matching names in input order. These patterns are not regular expressions: sales-??.csv also accepts sales-ab.csv.

Require digits or a literal question mark

Save as demo_fnmatch_2.py:

from fnmatch import fnmatchcase

pattern = "sales-[0-9][0-9].csv"
print("Digits:", fnmatchcase("sales-07.csv", pattern))
print("Letters:", fnmatchcase("sales-ab.csv", pattern))
print("Literal question mark:", fnmatchcase("draft?.txt", "draft[?].txt"))
print("Exclude zero:", fnmatchcase("part-3.txt", "part-[!0].txt"))

Run it from the directory where you saved it:

python3 demo_fnmatch_2.py

Output:

Digits: True
Letters: False
Literal question mark: True
Exclude zero: True

[0-9] matches one ASCII digit, while [!0] matches one character other than 0. Wrap a wildcard in brackets to match it literally. Two digit positions check the shape of a name, not whether its number is a valid calendar day.

Choose case and path rules deliberately

Save as demo_fnmatch_3.py:

from fnmatch import fnmatchcase

filename = "REPORT.CSV"
print("Exact case:", fnmatchcase(filename, "*.csv"))
print("Normalized:", fnmatchcase(filename.casefold(), "*.csv"))
print("Nested path:", fnmatchcase("archive/report.csv", "*.csv"))
print("Hidden name:", fnmatchcase(".private.csv", "*.csv"))

Run it from the directory where you saved it:

python3 demo_fnmatch_3.py

Output:

Exact case: False
Normalized: True
Nested path: True
Hidden name: True

fnmatch() follows platform case normalization; fnmatchcase() always compares case-sensitively. Normalize both name and pattern when your rules ignore case. Slashes and leading dots have no special protection. Match a path’s basename and exclude hidden names separately when needed. For filesystem discovery, use glob or pathlib.

References

Acknowledgements

This post was created with assistance from AI.

Last updated on