Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Matching Paths and Filenames with glob and fnmatch

Learn when to use glob to find pathnames and fnmatch to filter names, including recursive searches, hidden entries, case behavior, and literal wildcard characters.

Levels: Intermediate ~40 min read

Intermediate

Python has several ways to find or select names using shell-style wildcard patterns. The standard-library glob module finds pathnames in a directory tree; fnmatch tests strings that represent names. These tools are related, but they answer different questions from pathlib.Path.glob(), which returns Path objects.

Read wildcard patterns as patterns, not regular expressions

The common wildcard symbols are * for zero or more characters, ? for exactly one character, [seq] for one character from a set, and [!seq] for one character not in a set. This is shell-style wildcard syntax, not regular-expression syntax: for example, *.csv is not a regular expression.

Interactive Runnable example
Edit

Ready to run.

Find paths with glob

glob.glob() expands a pathname pattern and returns matching pathnames as strings. Pass root_dir when you want a search rooted at a directory without changing the process working directory. Relative patterns return names relative to that root. File-system order is not guaranteed, so sort matches whenever output, reports, or tests need predictable order.

Interactive Runnable example
Edit

Ready to run.

Search a tree recursively

The ** pattern can match zero or more directory levels, but glob.glob() uses recursive matching only when recursive=True. Recursive patterns can scan many directories, take substantial time, and in some patterns produce duplicate pathnames. Narrow root_dir and patterns before searching a large tree.

Interactive Runnable example
Edit

Ready to run.

Process matches lazily with iglob

glob.iglob() yields matches through an iterator instead of building a list of every pathname first. That can reduce memory use for large searches if you process each result as it arrives. It does not remove the cost of scanning the tree, and it has the same pattern and filesystem-error behavior as glob().

Interactive Runnable example
Edit

Ready to run.

Decide whether hidden names should match

By default, glob patterns such as *.txt do not match path components beginning with a dot. On Python 3.11 and later, include_hidden=True lets wildcard patterns match hidden path components too. Choose this option deliberately: hidden files may contain configuration, local state, or private data that should not be included in a bulk operation.

Interactive Runnable example
Edit

Ready to run.

Filter filename strings with fnmatch

fnmatch.filter(names, pattern) selects matching strings from an iterable of filename-like values. fnmatch.fnmatch() applies operating-system case normalization, so case behavior can vary by platform. Use fnmatch.fnmatchcase() when the comparison must always be case-sensitive. Unlike path globbing, fnmatch treats the path separator as an ordinary character: its job is to test a string, not to walk directories.

Interactive Runnable example
Edit

Ready to run.

Match literal names containing wildcard characters

When a path or name comes from data and contains characters such as *, ?, or [, those characters would normally be interpreted as pattern syntax. glob.escape() protects such characters in a pathname pattern so that the search can find a literal name. It is not a general-purpose shell-escaping function.

Interactive Runnable example
Edit

Ready to run.

Choose the right tool and understand its limits

Use pathlib.Path.glob() when you want Path objects and are already working with path objects. Use glob.glob() for a list of pathname strings, glob.iglob() for streaming matches, and fnmatch when filtering names already held in memory. glob suppresses OSError exceptions raised while scanning directories; if your task must report every inaccessible directory or prove a complete scan, do not assume glob alone provides that guarantee. For recursive searching, keep both the root and the pattern as narrow as the task allows.

Find names directly under a root

Complete the call so glob returns CSV pathnames rooted at root without changing the working directory.

import glob
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as temporary:
    root = Path(temporary)
    (root / "one.csv").touch()
    (root / "two.txt").touch()
    matches = sorted(glob.glob("*.csv", ))
    assert matches == ["one.csv"]
    print(matches)

Enable recursive ** matching

Complete the call so the double-star pattern also matches CSV files in child directories.

import glob
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as temporary:
    root = Path(temporary)
    (root / "nested").mkdir()
    (root / "nested" / "item.csv").touch()
    matches = glob.glob("**/*.csv", root_dir=str(root), )
    assert len(matches) == 1 and Path(matches[0]).name == "item.csv"
    print(matches[0])

Include hidden path entries

Complete the argument so *.txt can match both notes.txt and .draft.txt.

import glob
from pathlib import Path
from tempfile import TemporaryDirectory

with TemporaryDirectory() as temporary:
    root = Path(temporary)
    (root / "notes.txt").touch()
    (root / ".draft.txt").touch()
    matches = set(glob.glob("*.txt", root_dir=str(root), ))
    assert matches == {"notes.txt", ".draft.txt"}
    print(sorted(matches))

Choose a stable case-sensitive test

Complete the expression so REPORT.CSV does not match the lowercase pattern *.csv regardless of operating system.

import fnmatch

assert not fnmatch.("REPORT.CSV", "*.csv")
print("case-sensitive mismatch confirmed")

Protect a literal pattern character

Complete the expression so the brackets in release[final].csv are treated as literal characters.

import glob

name = "release[final].csv"
pattern = 
assert pattern == glob.escape(name)
print(pattern)
Was this page helpful?

Mark this learning resource complete to track your learning progress.