Find Files with Python `glob`

Learn how to use Python's glob module to search for files using wildcard patterns, recursive matching, and character classes.

6 min read

The glob module in the Python standard library finds file paths matching Unix-style wildcard patterns. You pass a pattern like ".py" or "data/**/.csv", and glob returns a list of matching paths. It is the simplest way to answer questions like "find all JSON files in this folder" or "list every Python file in the project tree."

pythonpython
import glob
 
py_files = glob.glob("*.py")
print(len(py_files))
for name in py_files[:3]:
    print(name)

The count comes first, then the loop prints up to three of the matching file names so you can see the actual results, not just how many there are:

texttext
3
main.py
utils.py
config.py

glob.glob("*.py") returns all files ending in .py in the current directory. The results are path strings, not Path objects. The order is arbitrary and depends on the filesystem.

Basic pattern syntax

Glob patterns use a small set of special characters that behave like wildcards.

PatternMatchesExample matches
*Any number of any characters (except /)*.txt matches readme.txt, notes.txt
?Exactly one characterfile?.csv matches file1.csv, fileA.csv
[abc]One character from the setreport_[12].pdf matches report_1.pdf
[!abc]One character not in the setfile_[!0-9].txt matches file_a.txt
[0-9]One character in the rangeimage_[0-9].png matches image_3.png
**Any number of directories (recursive)**/*.py matches all .py files at any depth

These patterns work identically in glob.glob(), pathlib.Path.glob(), and Unix shells.

Pattern examples

A few common patterns and what they find.

pythonpython
import glob
 
for pat in ["*.csv", "data/*.json", "report_202[5-6].pdf"]:
    matches = glob.glob(pat)
    print(f"{pat}: {len(matches)} matches")
    for m in matches[:2]:
        print(f"  {m}")

Each pattern prints its match count followed by up to two example file names, so you can see exactly which files each wildcard or character class picked out:

texttext
*.csv: 2 matches
  results.csv
  summary.csv
data/*.json: 1 matches
  data/config.json
report_202[5-6].pdf: 3 matches
  report_2025.pdf
  report_2026.pdf

report_202[5-6].pdf matches report_2025.pdf and report_2026.pdf but not report_2024.pdf. The brackets define a character class with the range 5 through 6.

Add recursive=True and use the ** pattern to search through all subdirectories.

pythonpython
import glob
 
all_py = glob.glob("**/*.py", recursive=True)
print(f"Found {len(all_py)} Python files")
 
for path in sorted(all_py)[:5]:
    print(path)

Sorting the paths first keeps the printed sample in a consistent, readable order even though glob itself does not guarantee one:

texttext
Found 47 Python files
src/__init__.py
src/models.py
src/utils.py
tests/test_models.py
tests/test_utils.py

** matches zero or more directories, so **/*.py finds .py files at every level from the current directory down. Without recursive=True, ** behaves like * and only matches within a single directory.

You can also search at a specific depth:

pythonpython
glob.glob("src/**/test_*.py", recursive=True)

This finds files like src/tests/test_models.py and src/integration/test_api.py, matching test_*.py anywhere under src/.

Using iglob for large results

glob.iglob() returns an iterator instead of a list. Use it when you expect many matches and do not want to store all paths in memory at once.

pythonpython
import glob
 
total_size = 0
for path in glob.iglob("**/*.csv", recursive=True):
    total_size += 1
 
print(f"Found {total_size} CSV files")

iglob produces paths one at a time, so the memory usage stays low even with millions of matching files. Use glob.glob() when you need random access or sorting; use glob.iglob() when you only need to iterate once.

Combining glob with other modules

A common pattern is to find files with glob and then process them with other standard library modules.

pythonpython
import glob
import json
 
for path in glob.glob("configs/*.json"):
    with open(path) as file:
        config = json.load(file)
    print(f"{path}: {config.get('name', 'unnamed')}")

This finds all JSON files in the configs directory and reads each one. For more on JSON, see Read and Write JSON Files in Python.

Another pattern: find and process CSV files.

pythonpython
import glob
import csv
 
for path in glob.glob("reports/**/*.csv", recursive=True):
    with open(path, newline="") as file:
        reader = csv.DictReader(file)
        row_count = sum(1 for _ in reader)
    print(f"{path}: {row_count} rows")

This counts rows in every CSV file under the reports directory. For more on CSV, see Work with CSV Files in Python.

glob vs pathlib.glob

The pathlib module has its own .glob() method that returns Path objects instead of strings. It is the recommended approach for new code.

pythonpython
from pathlib import Path
 
for path in Path(".").glob("*.py"):
    print(path.name)  # main.py, then utils.py

The pattern syntax is identical. The difference is the return type: Path.glob() returns Path objects, which let you call .name, .stem, .read_text(), and other Path methods directly.

glob.glob()Path.glob()
Return typeStringsPath objects
Pattern syntaxSame wildcardsSame wildcards
Chaining Path methodsRequires wrapping in Path()Direct (.name, .stem, .read_text())
Best forExisting glob-based codebasesNew code

Use glob.glob() when you are working in a codebase that already uses the glob module or when you specifically need string paths. Use Path.glob() in new code for consistency with the rest of pathlib.

Practical example: find and organize files by extension

Combine glob with shutil to find and organize files.

pythonpython
import glob
import shutil
from pathlib import Path
 
extensions = {"*.pdf": "pdfs", "*.png": "images", "*.csv": "data", "*.txt": "docs"}
 
for pattern, folder in extensions.items():
    Path(folder).mkdir(exist_ok=True)
    for filepath in glob.glob(f"downloads/{pattern}"):
        filename = Path(filepath).name
        shutil.move(filepath, f"{folder}/{filename}")
        print(f"Moved {filename} -> {folder}/")

The script finds files by extension in a downloads folder and moves each one to the corresponding category folder.

Each pattern uses glob to find matching files, and shutil.move handles the relocation.

Common mistakes

**Forgetting recursive=True with . Without recursive=True, ** behaves like * and only matches within a single directory. If your recursive search returns no results, check that you passed recursive=True.

Assuming sorted results. glob returns paths in filesystem order, which is not alphabetical. Always wrap with sorted() if you need a predictable order.

Expecting hidden files to match. Patterns like * and ? do not match a leading dot. To find hidden files, start the pattern with . (like .*) or use include_hidden=True in Python 3.11+.

Using glob for complex filename logic. Glob patterns are for filename matching, not for filtering by file size, modification date, or content. For those cases, find files with glob first, then filter the results with Python logic.

Rune AI

Rune AI

Key Insights

  • Use glob.glob('*.py') to find all Python files in the current directory.
  • Use glob.glob('**/*.py', recursive=True) to search all subdirectories.
  • Use character classes like [0-9] and [!x] for precise pattern matching.
  • glob.iglob() returns an iterator for memory-efficient processing of large results.
  • Sort results with sorted(); the return order is filesystem-dependent.
  • For Path objects and modern code, prefer pathlib.Path.glob().
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between glob and pathlib.glob?

Both use the same pattern syntax and both return results. `glob.glob()` returns strings. `Path.glob()` from `pathlib` returns Path objects and is the recommended approach for new code. The standalone `glob` module is older but still widely used and supported.

Does glob search hidden files?

By default, `glob` does not match files starting with a dot (hidden files on Unix). To include hidden files, add `include_hidden=True` (available since Python 3.11) or explicitly start your pattern with `.` like `glob.glob('.*')`.

How do I sort glob results?

The order of `glob` results is arbitrary and depends on the filesystem. Sort them with `sorted(glob.glob(pattern))` for alphabetical order, or use `sorted(..., key=os.path.getmtime)` to sort by modification time.

Conclusion

The glob module is the quickest way to find files by pattern in Python. Use * for any characters in a filename, ** for recursive directory search, and character classes like [0-9] for precise matching. For modern code, pathlib.Path.glob() provides the same patterns with Path objects, but the standalone glob module remains a solid choice for quick file searches.