您将如何在 python 中实现 ant 样式集来选择文件组?

How would you implement ant-style patternsets in python to select groups of files?

提问人:izb 提问时间:10/2/2008 更新时间:11/13/2014 访问量:3578

问:

Ant 有一种很好的方法来选择文件组,最方便的是使用 ** 来指示目录树。例如

**/CVS/*            # All files immediately under a CVS directory.
mydir/mysubdir/**   # All files recursively under mysubdir

更多示例可以在这里看到:

http://ant.apache.org/manual/dirtasks.html

您将如何在 python 中实现这一点,以便您可以执行以下操作:

files = get_files("**/CVS/*")
for file in files:
    print file

=>
CVS/Repository
mydir/mysubdir/CVS/Entries
mydir/mysubdir/foo/bar/CVS/Entries
python 文件 ant

评论


答:

2赞 dkagedal 10/2/2008 #1

os.walk是你的朋友。查看 Python 手册中的示例 (https://docs.python.org/2/library/os.html#os.walk)并尝试从中构建一些东西。

若要将“”与获得的文件名进行匹配,可以执行如下操作:**/CVS/*

def match(pattern, filename):
    if pattern.startswith("**"):
        return fnmatch.fnmatch(file, pattern[1:])
    else:
        return fnmatch.fnmatch(file, pattern)

在 中,“*”匹配任何内容(包括斜杠)。fnmatch.fnmatch

评论

0赞 izb 10/2/2008
这对于枚举文件树来说很好。问题在于确定特定文件是否与给定的搜索模式匹配。例如,“/mydir/CVS/Entries”是否与给定的“*/CVS/”模式匹配?
0赞 Aaron Maenpaa 10/2/2008
是的,但是一旦您开始操作,匹配文件只需将文件集表达式转换为合适的正则表达式并过滤结果即可。
0赞 ayaz 10/2/2008 #2

是的。正如已经建议的那样,你最好的选择是使用'os.walk'。或者,也许可以围绕“glob”和“fnmatch”模块编写包装器。

0赞 Jack M. 10/2/2008 #3

os.walk 是您最好的选择。我用.svn做了下面的例子,因为我有这个方便的东西,而且效果很好:

import re

for (dirpath, dirnames, filenames) in os.walk("."):
    if re.search(r'\.svn$', dirpath):
        for file in filenames:
            print file
3赞 Brian 10/3/2008 #4

一旦你遇到一个,你将不得不递归整个目录结构,所以我认为在这一点上,最简单的方法是使用 os.walk 遍历目录,构造一个路径,然后检查它是否与模式匹配。您可以通过以下方式转换为正则表达式:**

def glob_to_regex(pat, dirsep=os.sep):
    dirsep = re.escape(dirsep)
    print re.escape(pat)
    regex = (re.escape(pat).replace("\\*\\*"+dirsep,".*")
                           .replace("\\*\\*",".*")
                           .replace("\\*","[^%s]*" % dirsep)
                           .replace("\\?","[^%s]" % dirsep))
    return re.compile(regex+"$")

(但请注意,这并不是那么完整的功能 - 例如,它不支持样式 glob 模式,尽管这可能会被添加)。(第一个匹配是涵盖匹配等情况,以及必须在尾部匹配。[a-z]\*\*/\*\*/CVS./CVS\*\*

但是,显然,在不处理模式时,您不希望递归当前目录下的所有内容,因此我认为您需要一个两阶段方法。我还没有尝试过实现以下内容,并且可能有一些极端情况,但我认为它应该有效:**

  1. 拆分目录分隔符上的模式。即pat.split('/') -> ['**','CVS','*']

  2. 递归目录,并查看此级别的模式的相关部分。即。.n levels deep -> look at pat[n]

  3. 如果切换到上述策略:pat[n] == '**'

    • dirsep.join(pat[n:])
    • 转换为正则表达式glob\_to\_regex()
    • 递归地遍历当前目录,建立相对于您开始的级别的路径。如果路径与正则表达式匹配,则让出它。os.walk
  4. 如果 pat 不匹配,并且它是模式中的最后一个元素,则生成所有匹配的文件/目录"**"glob.glob(os.path.join(curpath,pat[n]))

  5. 如果 pat 不匹配,并且它不是模式中的最后一个元素,则对于每个目录,检查它是否匹配(使用 glob)。如果是这样,请向下递归,增加深度(这样它就会查看"**"pat[n]pat[n+1])

1赞 GoraKhargosh 12/24/2010 #5

“waf”构建系统源代码中有一个实现。http://code.google.com/p/waf/source/browse/trunk/waflib/Node.py?r=10755#471也许这应该被包装在自己的库中?

5赞 Andrew Alcock 5/15/2012 #6

对不起,这是您 OP 之后很长一段时间。我刚刚发布了一个 Python 包,它完全可以做到这一点 - 它被称为 Formic,可以在 PyPI Cheeseshop 上找到。使用 Formic,您的问题可以通过以下方式解决:

import formic
fileset = formic.FileSet(include="**/CVS/*", default_excludes=False)
for file_name in fileset.qualified_files():
    print file_name

有一个轻微的复杂性:default_excludes。Formic,就像 Ant 一样,默认排除 CVS 目录(因为在大多数情况下,从它们收集文件进行构建是危险的),问题的默认答案将导致没有文件。设置 default_excludes=False 将禁用此行为。

评论

0赞 cod3monk3y 2/18/2014
使用起来非常简单的库。谢谢安德鲁。我不得不使用它而不是让它在 Python 2.7.6 中正确安装。否则就像一个魅力。为我节省了大量的工作(让我删除了大约 50 行代码)。easy_installpipvirtualenv