fnmatch 和递归路径匹配 `**`

Posted

技术标签:

【中文标题】fnmatch 和递归路径匹配 `**`【英文标题】:fnmatch and recursive path match with `**` 【发布时间】:2013-08-22 21:43:53 【问题描述】:

是否有任何内置或直接的方法可以用双星号递归匹配路径,例如像zsh 一样吗?

例如,用

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

我可以使用fnmatch 来测试它

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

虽然,我希望能够做到:

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

我知道 fnmatch maps its pattern to regex,所以在这种情况下,我可以使用额外的 ** 模式滚动我自己的 fnmatch,但也许有更简单的方法

【问题讨论】:

类似glob.glob? 这是一个允许 fnmatch * 和 ** pypi.python.org/pypi/pywildcard 的分支 【参考方案1】:

如果您仔细查看 fnmatch 源代码,它会在内部将模式转换为正则表达式,将 * 映射为 .*(而不是 [^/]* 或类似的),因此不关心目录分隔符 @987654324 @ - 与 UNIX shell 不同:

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

【讨论】:

【参考方案2】:

如果您可以不使用 os.walk 循环,请尝试:

glob2

formic

我个人使用 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) 

【讨论】:

问题是关于使用路径(可能是虚拟的),而 glob 正在使用扫描真实文件系统。【参考方案3】:

这个 sn-p 增加了对 ** 的兼容性

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

【讨论】:

这在 py3.9 中似乎不起作用。看来 fnmatch 现在会过滤掉连续的星号【参考方案4】:

对于适用于路径的 fnmatch 变体,您可以使用一个名为 wcmatch 的库,它实现了一个 globmatch 函数,该函数与 glob 爬取文件系统所用的逻辑相同的路径匹配。您可以使用标志来控制启用的功能,在这种情况下,我们启用GLOBSTAR(使用** 进行递归目录搜索)。

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

【讨论】:

谢谢!正是我正在寻找的fr :) 很高兴看到人们发现这个库很有用:)。我花了很多时间来开发它。 继续努力,我也是开源作者/维护者 :)

以上是关于fnmatch 和递归路径匹配 `**`的主要内容,如果未能解决你的问题,请参考以下文章

获取与 fnmatch 不匹配的元素

python--fnmatch

Python: 用shell通配符匹配字符串,fnmatch/fnmatchcase

路径信息与 fnmatch

在 python 中将普通文件名与 fnmatch 模式分开

Python——模块——fnmatch(文件名对比)