searchig for a file in sd

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
ttadam
Posts: 18
Joined: Sun Apr 26, 2020 3:36 pm

searchig for a file in sd

Post by ttadam » Mon May 18, 2020 3:17 pm

Hello there,

Is there a way to search for a file everywhere in the SD card with micropython(pycom to be specific)?
In my application there is config.json file. And a want to provide a feature where if the config file is not where it should be, then the sytem ask the and user to search for it other places.
I think I can do some recursive algorithm with os.listdir() but I am wondering is there a more elegant and optimal way?

Thanks in advance! :)

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: searchig for a file in sd

Post by dhylands » Mon May 18, 2020 4:47 pm

The os.walk function from python is implemented for the unix version MicroPython over here: https://github.com/micropython/micropyt ... #L147-L163

It probably needs a few tweaks to make it work on embedded MicroPython. os.walk behaves like a generator so you can enumerate all of the files with a single for loop. The documentation for os.walk is here: https://docs.python.org/2/library/os.html#os.walk

You could also use a customized version to do exactly what you want with less code.

bitninja
Posts: 165
Joined: Thu Sep 15, 2016 4:09 pm
Location: Spring, Texas

Re: searchig for a file in sd

Post by bitninja » Mon May 18, 2020 4:50 pm

Manually is all I know about...

Code: Select all

import os

def merge(*args):
    r = ''
    slash = '/'
    for part in args:
        r += (slash + part)
    r = r.replace(3 * slash, slash)
    r = r.replace(2 * slash, slash)
    if r != slash and r[-1] == slash:
        r = r[:-1]
    return r

def find(target_file, path='/', recurse=False):
    # create list of files
    file_list = list(os.ilistdir(path))
    # only recurse through the dirs if needed
    if recurse:
        for f in file_list:
            if f[1] == 16384:
                find(target_file, merge(path, f[0]), recurse)
    # scan the files
    for f in file_list:
        if f[1] == 32768:
            if f[0] == target_file:
                hit = merge(path, f[0])
                print(hit)

#find('__init__.py', '/', True)

SpotlightKid
Posts: 463
Joined: Wed Apr 08, 2015 5:19 am

Re: searchig for a file in sd

Post by SpotlightKid » Tue May 19, 2020 12:19 pm

Using os.ilistdir() only to convert the return value to a list isn't very effective and can lead to memory errors when searching directories with many entries.

Here's my version of a find function, which acts as an iterator, i.e. yields matching files (with full path) one by one. It recurses by default. If there's a deeply nested directory structure beneath the starting directory it may therefor hit the recursion limit of MicroPython. But I think that's highly unlikely when used on a microprocessor board flash filesystem and a non-recursive solution would probably run into a memory error much faster. Anyway, if you're concerned, that the recursion limit may be hit, just wrap the call in a try/except RuntimeError.

Code: Select all

import os
from micropython import const


STAT_DIR = const(0x4000)


def pathjoin(*parts):
    path = []
    for part in parts:
        part = part.rstrip("/")
        if part.startswith("/"):
            path = [part]
        else:
            path.append(part)
    return "/".join(path)


def find(filename, path="/", recurse=True):
    for name, type_, *_ in os.ilistdir(path):
        # unix port includes these:
        if name in ('.', '..'):
            continue
        # only recurse through the dirs if needed
        if recurse and type_ & STAT_DIR:
            yield from find(filename, pathjoin(path, name), recurse)
        # check the file
        elif name == filename:
            yield pathjoin(path, name)
Example usage:

Code: Select all

try:
    for name in find('main.py', '/flash'):
        print(name)
except RuntimeError:
    print("Too many levels of sub-directories.")

ttadam
Posts: 18
Joined: Sun Apr 26, 2020 3:36 pm

Re: searchig for a file in sd

Post by ttadam » Tue Nov 10, 2020 10:46 am

Thank you! :)
SpotlightKid wrote:
Tue May 19, 2020 12:19 pm
Using os.ilistdir() only to convert the return value to a list isn't very effective and can lead to memory errors when searching directories with many entries.

Here's my version of a find function, which acts as an iterator, i.e. yields matching files (with full path) one by one. It recurses by default. If there's a deeply nested directory structure beneath the starting directory it may therefor hit the recursion limit of MicroPython. But I think that's highly unlikely when used on a microprocessor board flash filesystem and a non-recursive solution would probably run into a memory error much faster. Anyway, if you're concerned, that the recursion limit may be hit, just wrap the call in a try/except RuntimeError.

Code: Select all

import os
from micropython import const


STAT_DIR = const(0x4000)


def pathjoin(*parts):
    path = []
    for part in parts:
        part = part.rstrip("/")
        if part.startswith("/"):
            path = [part]
        else:
            path.append(part)
    return "/".join(path)


def find(filename, path="/", recurse=True):
    for name, type_, *_ in os.ilistdir(path):
        # unix port includes these:
        if name in ('.', '..'):
            continue
        # only recurse through the dirs if needed
        if recurse and type_ & STAT_DIR:
            yield from find(filename, pathjoin(path, name), recurse)
        # check the file
        elif name == filename:
            yield pathjoin(path, name)
Example usage:

Code: Select all

try:
    for name in find('main.py', '/flash'):
        print(name)
except RuntimeError:
    print("Too many levels of sub-directories.")

Post Reply