There are a couple of ways:

pathlib.Path().rglob()

Use pathlib.Path().rglob() from the pathlib module, which was introduced in Python 3.5.

from pathlib import Path

for path in Path('src').rglob('*.c'):
    print(path.name)

glob.glob()

If you don't want to use pathlib, use glob.glob():

from glob import glob

for filename in glob('src/**/*.c', recursive=True):
    print(filename)   

For cases where matching files beginning with a dot (.); like files in the current directory or hidden files on Unix based system, use the os.walk() solution below.

os.walk()

For older Python versions, use os.walk() to recursively walk a directory and fnmatch.filter() to match against a simple expression:

import fnmatch
import os

matches = []
for root, dirnames, filenames in os.walk('src'):
    for filename in fnmatch.filter(filenames, '*.c'):
        matches.append(os.path.join(root, filename))

This version should also be faster depending on how many files you have, as the pathlib module has a bit of overhead over os.walk().

Answer from Johan Dahlin on Stack Overflow
🌐
GeeksforGeeks
geeksforgeeks.org β€Ί python β€Ί how-to-use-glob-function-to-find-files-recursively-in-python
How to use Glob() Function to Find Files Recursively in Python - GeeksforGeeks
October 3, 2025 - From Python 3.5 onwards, the glob module supports recursive searching using the ** pattern combined with the recursive=True argument.
🌐
Medium
karliris62.medium.com β€Ί how-to-use-glob-function-to-find-files-recursively-in-python-b746af8afbc2
How to use Glob() function to find files recursively in Python? | by Gaurav Roy CTO, Masters | BS-Cyber-Sec | MIT | LPU | Medium
August 18, 2022 - From Python 3.5 onwards, programmers can use the Glob() function to find files recursively. In Python, the glob module plays a significant role in retrieving files & pathnames that match with the specified pattern passed as its parameter.
🌐
Note.nkmk.me
note.nkmk.me β€Ί home β€Ί python
How to Use glob() in Python | note.nkmk.me
July 28, 2023 - Use glob() to generate a list of files and directories recursively, then apply re.search() to this list.
🌐
Real Python
realpython.com β€Ί ref β€Ί stdlib β€Ί glob
glob | Python Standard Library – Real Python
>>> import glob >>> image_files = glob.glob('**/*.jpg', recursive=True) + \ ... glob.glob('**/*.png', recursive=True) + \ ... glob.glob('**/*.gif', recursive=True) ... >>> image_files ['images/photo.jpg', 'graphics/logo.png', 'pictures/image.gif'] This snippet demonstrates how to use the glob module to locate image files in a directory tree by using pattern matching. ... In this tutorial, you'll be examining a couple of methods to get a list of files and folders in a directory with Python.
🌐
Built In
builtin.com β€Ί software-engineering-perspectives β€Ί glob-in-python
Glob in Python Explained | Built In
Adding the argument recursive=True tells glob to search all sub-directories as well as the ml_guides directory.
Find elsewhere
🌐
Profound Academy
profound.academy β€Ί python-mid β€Ί glob-Ljj9pCI0SAZYN6czWUGq
glob β€’ Intermediate Python
January 21, 2025 - nested_files = glob.glob('**/*.txt', recursive=True) # Fetches all .txt files recursively print(nested_files, type(nested_files)) # Prints the list of .txt files and its type
🌐
Learn By Example
learnbyexample.org β€Ί python-glob-glob-function
Python glob.glob() Function - Learn By Example
April 14, 2024 - Here are some pattern rules you can use with glob.glob(): Recursive Search: When recursive is set to True, ** will match any files and zero or more directories, subdirectories, and symbolic links to directories.
🌐
STechies
stechies.com β€Ί glob-function-find-files-recursively-python
How to use Glob() function to find files recursively in Python?
From Python 3.5 onwards, programmers can use the Glob() function to find files recursively. In Python, the glob module plays a significant role in retrieving files & pathnames that match with the specified pattern passed as its parameter.
🌐
PYnative
pynative.com β€Ί home β€Ί python β€Ί python glob: filename pattern matching
Python Glob: Filename Pattern Matching – PYnative
June 17, 2021 - When you set a recursive flag to True, the glob method parses the given path look recursively in the directories.
🌐
Pythonology
pythonology.eu β€Ί glob-module-file-searching-in-python
GLOB Module: File Searching in Python – pythonology
Sometimes, you may want to search for files not only in a single directory but also in its subdirectories. The glob module makes it easy to search for files recursively using the ** globbing pattern in glob.glob().
🌐
GeeksforGeeks
geeksforgeeks.org β€Ί python β€Ί python-list-all-files-in-directory-and-subdirectories
Python List All Files In Directory And Subdirectories - GeeksforGeeks
July 23, 2025 - In this example, the Python function `list_files_glob` uses the `glob` module to list and print the full paths of files matching a specified pattern (`'./**/*'` by default). The `recursive` parameter is set to `True` by default, allowing it to recursively search subdirectories.
🌐
Codecademy
codecademy.com β€Ί docs β€Ί python β€Ί glob module β€Ί .glob()
Python | Glob Module | .glob() | Codecademy
June 25, 2022 - ... The pathname must be a valid string that represents a file path that can either be absolute or relative. By default, recursive is set to False. But when set to True, the pattern ** can be used to match any files and folders in a given directory.
Top answer
1 of 2
1

I think you have misunderstood the meaning of recursive=True. It is used to allow ** to mean searching subdirectories to a variable depth -- glob(folder+'\\**\\*.ext',recursive=True) will find any *.ext files in the starting folder or anywhere below it. Without recursive=True, ** would be interpreted in the normal way, with each * meaning to match any number of characters excluding \, and ** would therefore just be equivalent to * -- in other words glob(folder+'\\**\\*.ext',recursive=False) would search for any *.ext files that are exactly one level of subdirectory down from the starting folder, but not in the starting folder itself or in deeper subdirectories.

Exactly which you should use will depend on what you are trying to look for -- and it may indeed be that you need to select between different cases with an if statement -- but there is no use case for using ** with recursive=False, because it is just equivalent to * in that case.

The most likely useful options are:

glob(folder+'\\*.ext',recursive=False)  # search in top folder only
glob(folder+'\\*\\*.ext',recursive=False)  # search exactly one level down
glob(folder+'\\**\\*.ext',recursive=True)  # search to arbitrary depth

The ,recursive=False can be omitted because it is the default.

If there were exactly two options that you wanted to select between using a boolean option, you could always write your own wrapper function, for example:

def myglob(folder, pattern, subdirs=False):
    if subdirs:
        return glob(f'{folder}\\**\\{pattern}', recursive=True)
    else
        return glob(f'{folder}\\{pattern}')

print(myglob(folder, '*.ext', subdirs=False))
print(myglob(folder, '*.ext', subdirs=True))
print(myglob(folder, '*.ext'))  # same as subdirs=False in this example

(In Unix-like operating systems, read / for \ above.)

2 of 2
0

Operation of glob '**'

The glob source code can be found here. Other than comments, '**' only appears in the _isrecursive function:

def _isrecursive(pattern):
    if isinstance(pattern, bytes):
        return pattern == b'**'
    else:
        return pattern == '**'

Because the _isrecursive function checks for '' using the equality operator, '' will only operate as intended if it is the entire 'pattern'. The _isrecursive function is called 4 times in the source code, once for the entire glob and 3 times for 'basename', which is defined as:

dirname, basename = os.path.split(pathname)

This is called recursively, splitting tails from the path one at a time, and each time, basename is everything following the slash (technically os.sep,which is \ for Windows or / for POSIX). That means that in order for '**' to operate as described, it may not be adjacent to anything other than slashes.

If recursive is not explicitly set to True, '**' = '*'+'*' = '*' since '*' matches 0 or more non-slash characters.

Matching Extensions

Because of how '**' operates, it must be used to represent an entire level of the glob. Since it represents 0 or more levels, r'folder\**\*.ext' will match files in 'folder', as well as subfolders. However, without recursive set to true, this pattern will match only files in the first level of subfolders.

'**.ext' will not work because '**' isn't alone. This leaves the following options:

  1. Use a different library (eg. os.walk, pathlib.glob, etc.)

  2. [f for f in glob(folder+'\\**',recursive=subdirs) if f.endswith('.ext')]

  3. glob(folder+('\\**' if subdirs else '')+'\\*.ext, recursive=True)

    Or equivalently,

    if recursive:
        glob(folder+'\\**\\*.ext', recursive=True)
    else:
        glob(folder+'\\*.ext')
    
  4. glob(folder+'\\..\\**\\*.ext', recursive=subdirs) #not recommended since it will also match files in sibling folders

🌐
ChristopherGS
christophergs.com β€Ί python β€Ί 2017 β€Ί 01 β€Ί 01 β€Ί python-glob
Using Python glob
January 1, 2017 - Starting with Python version 3.5, the glob module supports the β€œ**” directive (which is parsed only if you pass recursive flag).
🌐
Chris Piekarski
cpiekarski.com β€Ί 2011 β€Ί 09 β€Ί 23 β€Ί python-recursive-glob
Python – Recursive Glob & Line Counter | Chris Piekarski
December 15, 2017 - """ Recursive Glob Module Methods: rglob(base, pattern) rglob_(pattern) lcount(base, pattern, func=lambda x : True) """ import glob import os def _getDirs(base): return [x for x in glob.iglob(os.path.join( base, '*')) if os.path.isdir(x) ] def _count(files, func): lines = 0 for f in files: lines += sum([1 for l in open(f) if func(l)]) return lines def rglob(base, pattern): """ Recursive glob starting in specified directory """ flist = [] flist.extend(glob.glob(os.path.join(base,pattern))) dirs = _getDirs(base) if len(dirs): for d in dirs: flist.extend(rglob(os.path.join(base,d), pattern)) return flist def rglob_(pattern): """ Performs a recursive glob in the current working directory """ return rglob(os.getcwd(), pattern) def lcount(base, pattern, func = lambda x : True): """ Counts the number of lines in each file found matching pattern.