'Python: search for a file in current directory and all it's parents

Is there an inbuilt module to search for a file in the current directory, as well as all the super-directories?

Without the module, I'll have to list all the files in the current directory, search for the file in question, and recursively move up if the file isn't present. Is there an easier way to do this?



Solution 1:[1]

Another option, using pathlib:

from pathlib import Path


def search_upwards_for_file(filename):
    """Search in the current directory and all directories above it 
    for a file of a particular name.

    Arguments:
    ---------
    filename :: string, the filename to look for.

    Returns
    -------
    pathlib.Path, the location of the first file found or
    None, if none was found
    """
    d = Path.cwd()
    root = Path(d.root)

    while d != root:
        attempt = d / filename
        if attempt.exists():
            return attempt
        d = d.parent

    return None

Solution 2:[2]

Here's another one, using pathlib:

from pathlib import Path


def find_upwards(cwd: Path, filename: str) -> Path | None:
    if cwd == Path(cwd.root):
        return None
    
    fullpath = cwd / filename
    
    return fullpath if fullpath.exists() else find_upwards(cwd.parent, filename)
    
    
find_upwards(Path.cwd(), "helloworld.txt")

(using some Python 3.10 typing syntax here, you can safely skip that if you are using an earlier version)

Solution 3:[3]

The parent question was to walk parent directories (not descend into children like the find command):

# walk PARENT directories looking for `filename`:

f = 'filename'
d = os.getcwd()

while d != "/" and f not in os.listdir(d):
    d = os.path.abspath(d + "/../")

if os.path.isfile(os.path.join(d,f)):
    do_something(f)

Here's a version that uses shell globbing to match multiple files:

# walk PARENT directories looking for any *.csv files,
# stopping when a directory that contains any:

f = '*.csv'
d = os.getcwd()

while d != "/" and not glob.glob(os.path.join(d, f)):
    d = os.path.abspath(d + "/../")

files = glob.glob(os.path.join(d,f))

for filename in files:
    do_something(filename)

Solution 4:[4]

Here a function that does an upward search:

import sys, os, os.path
def up_dir(match,start=None):
    """
    Find a parent path producing a match on one of its entries.
    Without match an empty string is returned.

    :param match: a function returning a bool on a directory entry
    :param start: absolute path or None
    :return: directory with a match on one of its entries

    >>> up_dir(lambda x: False)
    ''

    """

    if start is None:
        start = os.getcwd()
    if any(match(x) for x in os.listdir(start)):
        return start
    parent = os.path.dirname(start)
    if start == parent:
        rootres = start.replace('\\','/').strip('/').replace(':','')
        if len(rootres)==1 and sys.platform=='win32':
            rootres = ''
        return rootres
    return up_dir(match,start=parent)

Solution 5:[5]

Here is an example that will find all the .csv files in a specified directory "path" and all its root directories and print them:

    import os
    for root, dirs, files in os.walk(path):
        for file in files:
            if file.endswith(".csv"):
                path_file = os.path.join(root,file)
                print(path_file)

If you want to start at one directory and work your way through the parents then this would work for finding all the .csv files (for example):

import os
import glob
last_dir = ''
dir = r'c:\temp\starting_dir'

os.chdir(dir)
while last_dir != dir:
    dir = os.getcwd()
    print(glob.glob('*.csv'))
    os.chdir('..')
    last_dir = os.getcwd()   

Solution 6:[6]

I was looking for this too, since os.walk is exactly the opposite of what I wanted. That searches subdirectories. I wanted to search backwards through parent directories until I hit the drive root.

Bumming some inspiration from previous answers, below is what I am using. It doesn't require changing the working directory and it has a place for you to do something when you find a match. And you can change how the match is found. I'm using regex but a basic string compare would work fine too.

# Looking for a file with the string 'lowda' in it (like beltalowda or inyalowda)
import os
import re # only if you want to use regex

# Setup initial directories
starting_dir = 'C:\\Users\\AvasaralaC\\Documents\\Projects'
last_dir = ''
curr_dir = starting_dir
filename = ''

# Loop through parent directories until you hit the end or find a match
while last_dir != curr_dir:
    for item in os.listdir(curr_dir):
        if re.compile('.*lowda.*').search(item): # Here you can do your own comparison
            filename = (curr_dir + os.path.sep + item)
            break
    if filename:
        break
    last_dir = curr_dir
    curr_dir = os.path.abspath(curr_dir + os.path.sep + os.pardir)

Other comparisons you could do are item.lower().endswith('.txt') or some other string comparison.

Solution 7:[7]

Just wrote this to find the "images" directory, note '/' is Linux style

dir = os.getcwd()
    while dir != '/' and not glob.glob( dir + '/images' ):
        dir = os.path.dirname(dir)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3
Solution 4
Solution 5 fatal_error
Solution 6
Solution 7 Kjeld Flarup