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."
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:
3
main.py
utils.py
config.pyglob.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.
| Pattern | Matches | Example matches |
|---|---|---|
| * | Any number of any characters (except /) | *.txt matches readme.txt, notes.txt |
| ? | Exactly one character | file?.csv matches file1.csv, fileA.csv |
| [abc] | One character from the set | report_[12].pdf matches report_1.pdf |
| [!abc] | One character not in the set | file_[!0-9].txt matches file_a.txt |
| [0-9] | One character in the range | image_[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.
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:
*.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.pdfreport_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.
Recursive search
Add recursive=True and use the ** pattern to search through all subdirectories.
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:
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:
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.
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.
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.
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.
from pathlib import Path
for path in Path(".").glob("*.py"):
print(path.name) # main.py, then utils.pyThe 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 type | Strings | Path objects |
| Pattern syntax | Same wildcards | Same wildcards |
| Chaining Path methods | Requires wrapping in Path() | Direct (.name, .stem, .read_text()) |
| Best for | Existing glob-based codebases | New 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.
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
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().
Frequently Asked Questions
What is the difference between glob and pathlib.glob?
Does glob search hidden files?
How do I sort glob results?
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.
More in this topic
What Is the Python Standard Library?
Learn what the Python standard library is, why it matters, and how to use its built-in modules without installing anything extra.
Python `math` Module Explained
Learn how to use the Python math module for square roots, rounding, trigonometry, logarithms, and mathematical constants.
Format Dates and Times in Python
Learn how to use strftime and strptime to format dates as strings and parse date strings into Python datetime objects.