The fnmatch module provides tools for comparing file names using wildcard as specified by the Unix shell rules. Briefly, some of the patterns are outlined below:

* Matches zero or more characters, eg. *.txt to match all filenames ending with .txt.
? Matches a single character, e.g file?.txt matches file1.txt, file2.txt, etc
[seq] Matches any character in seq
[!seq] Matches any character not in seq.

The fnmatch module exists in the standard library, we just need to import it to start using it.

import fnmatch

print(fnmatch)

Basic Usage

The fnmatch module defines fnmatch() function which compares a single filename against a pattern. It returns True if the filename matches the pattern and False otherwise.

The fnmatch() function has the following syntax:

fnmatch.fnmatch(name, pattern)

Not that fnmatch() and all other functions in the the module do not validate that the files actually exists in the file system, they operate on mere strings. 

Consider the following example:

import fnmatch

filenames = ['basic.py', 'simple.txt', 'hello.py', 'initial.py', 'home.txt']
pat = "*.py"

for f in filenames:
    result = fnmatch.fnmatch(f, pat)
    print(f,': ', result)

In the above case, we entered the filenames manually. In practical scenarios, you can use the os.listdir() function to automate  retrieval of names that exists in a given directory.

Consider if we have a directory with the following structure:

project
├── main.py
├── test1.py  
├── demo.txt
├── test2.py
├── test4.txt
├── test.py
└── test3.py

In the following example, the fnmatch() function matches the files that starts with test and ends with .py.

import os, fnmatch

filenames = os.listdir('project')
print(filenames)
pat = "test*.py"

print("Matched filenames:")
for f in filenames:
	result = fnmatch.fnmatch(f, pat)
	if result == True:
	    print(f) 

['demo.txt', 'main.py', 'test.py', 'test1.py', 'test2.py', 'test3.py', 'test4.txt']
Matched filenames:
test.py
test1.py
test2.py
test3.py

Strict case sensitive comparison

The fnmatch() function, perform a case-sensitive comparison, if the operating system used uses a case-sensitive file system. Some operating systems do not use case-sensitive file systems.

The fnmachcase() performs a case sensitive comparison regardless of whether the operating system uses a case sensitive file system or not. It has similar syntax and works like fnmatch() function in all other aspects.

Consider if we have the following directory:

project
├── main.py
├── test1.py  
├── demo.txt
├── TEST2.py
├── test4.txt
├── test.py
└── TEST3.py

Using fnmatchcase() 

import os, fnmatch

filenames = os.listdir('project')
print(filenames)
pat = "test*.py"

print("Matched filenames:")
for f in filenames:
	result = fnmatch.fnmatchcase(f, pat) #call fnmatchcase
	if result == True:
	    print(f) 

['demo.txt', 'main.py', 'test.py', 'test1.py', 'TEST2.py', 'TEST3.py', 'test4.txt']
Matched filenames:
test.py
test1.py

As you can see in the above example, the files are ,matched case-sensitively based on the pattern.

Filtering filenames

The fnmatch.filter() function allows you to conveniently do what we did in the previous examples without using a loop, i.e test multiple names and return only those that matches the patten.

The function has the following syntax:

fnmatch.filter(names, pattern)
names A list of filenames to be tested.
pattern The pattern

The function returns a list of names that matched the pattern.

project
├── main.py
├── test1.py  
├── demo.txt
├── test2.py
├── test4.txt
├── test.py
└── test3.py

Using filter() 

import os, fnmatch

filenames = os.listdir('project')
pat = "test*.py"

matched = fnmatch.filter(filenames, pat)

print('All filenames: ')
print(filenames)
print("Matched filenames: ")
print(matched)

All filenames: 
['demo.txt', 'main.py', 'test.py', 'test1.py', 'test2.py', 'test3.py', 'test4.txt']

Matched filenames: 
['test.py', 'test1.py', 'test2.py', 'test3.py']

Translate Patterns to regular expressions

When you use a pattern with wildcards such as *, ?, etc,  the pattern is actually translated to a valid regular expression using the re module. You can use the fnmatch.translate() function, to view the translated regular expression for a given pattern.

import fnmatch

pat1 = '*'
pat2 = 'test*.py'
pat3 ='test?.py'

#get the regular expresssions
for pat in (pat1, pat2, pat3):
   print(f'{pat}: ', fnmatch.translate(pat))