pythonglobfnmatch

fnmatch and recursive path match with `**`


Is there any built-in or straightforward way to match paths recursively with double asterisk, e.g. like zsh does?

For example, with

path = 'foo/bar/ham/spam/eggs.py'

I can use fnmatch to test it with

fnmatch(path, 'foo/bar/ham/*/*.py'

Although, I would like to be able to do:

fnmatch(path, 'foo/**/*.py')

I know that fnmatch maps its pattern to regex, so in the words case I can roll my own fnmatch with additional ** pattern, but maybe there is an easier way


Solution

  • If you look into fnmatch source code closely, it internally converts the pattern to a regular expression, mapping * into .* (and not [^/]* or similar) and thus does not care anything for directory separators / - unlike UNIX shells:

    while i < n:
        c = pat[i]
        i = i+1
        if c == '*':
            res = res + '.*'
        elif c == '?':
            res = res + '.'
        elif c == '[':
            ...
    

    Thus

    >>> fnmatch.fnmatch('a/b/d/c', 'a/*/c')
    True
    >>> fnmatch.fnmatch('a/b/d/c', 'a/*************c')
    True