fnmatch и рекурсивное совпадение пути с `**`

Есть ли какой-либо встроенный или простой способ рекурсивного сопоставления путей с двойной звездочкой, например, как это делает zsh?

Например, с

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

Я могу использовать fnmatch, чтобы проверить это с

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

Хотя я хотел бы иметь возможность сделать:

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

Я знаю, что fnmatch отображает свой шаблон на регулярное выражение, поэтому в случае слов я могу бросить свой собственный fnmatch с дополнительными ** шаблон, но, возможно, есть более простой способ

2 ответа

Решение

Если вы внимательно посмотрите на исходный код fnmatch, он отобразит * в .* и, следовательно, не заботится о разделителях каталогов / - в отличие от оболочек UNIX:

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

таким образом

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

Для варианта fnmatch, который работает с путями, вы можете использовать библиотеку под названием wcmatch, которая реализуетglobmatch функция, которая соответствует пути с той же логикой, что и globсканирует файловую систему с помощью. Вы можете управлять включенными функциями с помощью флагов, в этом случае мы включаемGLOBSTAR (с помощью ** для рекурсивного поиска в каталоге).

>>> from wcmatch import glob
>>> glob.globmatch('some/file/path/filename.txt', 'some/**/*.txt', flags=glob.GLOBSTAR)
True

Если вы можете жить без использования цикла os.walk, попробуйте:

glob2

муравьиный

Я лично использую glob2:

import glob2
files = glob2.glob(r'C:\Users\**\iTunes\**\*.mp4')

Приложение:

Начиная с Python 3.5, собственный модуль glob поддерживает рекурсивное сопоставление с образцом:

import glob
files = glob.iglob(r'C:\Users\**\iTunes\**\*.mp4', recursive=True) 

Этот фрагмент добавляет совместимость для **

import re
from functools import lru_cache
from fnmatch import translate as fnmatch_translate


@lru_cache(maxsize=256, typed=True)
def _compile_fnmatch(pat):
    # fixes fnmatch for recursive ** (for compatibilty with Path.glob)
    pat = fnmatch_translate(pat)
    pat = pat.replace('(?s:.*.*/', '(?s:(^|.*/)')
    pat = pat.replace('/.*.*/', '.*/')
    return re.compile(pat).match


def fnmatch(name, pat):
    return _compile_fnmatch(str(pat))(str(name)) is not None
Другие вопросы по тегам