Как бы вы реализовали ant -образные шаблоны в python для выбора групп файлов?

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
6 ответов

Как только вы столкнетесь с **, вам придется переписывать всю структуру каталогов, поэтому я думаю, что в этот момент самым простым методом является итерация через каталог с помощью 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+"$")

(Хотя обратите внимание, что это не так полно, но не поддерживает шаблонные шаблоны стиля [a-z], хотя это, вероятно, можно добавить). (Первое совпадение \*\*/ должно охватывать такие случаи, как \*\*/CVS соответствие ./CVS, а также наличие только \*\* для соответствия в хвосте.)

Однако, очевидно, вы не хотите рекурсивно проходить через все ниже текущего каталога, когда не обрабатываете шаблон **, поэтому я думаю, вам понадобится двухфазный подход. Я не пробовал реализовать ниже, и, вероятно, есть несколько угловых случаев, но я думаю, что он должен работать:

  • Разделите шаблон на вашем разделителе каталогов. т.е. pat.split('/') -> ['**','CVS','*']

  • Пройдите через каталоги и посмотрите на соответствующую часть шаблона для этого уровня. то есть. n levels deep -> look at pat[n].

  • Если pat[n] == '**' переключиться на указанную выше стратегию:

    • Восстановить шаблон с помощью dirsep.join(pat[n:])
    • Преобразовать в регулярное выражение с помощью glob\_to\_regex()
    • Рекурсивно os.walk через текущий каталог, создавая путь относительно уровня, на котором вы начали. Если путь соответствует регулярному выражению, выведите его.
  • Если pat не соответствует "**", и это последний элемент в шаблоне, тогда выведите все файлы /dirs, соответствующие glob.glob(os.path.join(curpath,pat[n]))

  • Если pat не соответствует "**", и это НЕ последний элемент в шаблоне, то для каждого каталога проверьте, совпадает ли он (с glob) pat[n]. Если да, перейдите вниз через него, увеличивая глубину (так что он будет смотреть на pat[n+1])


Извините, это довольно долго после вашего 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 отключает это поведение.


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 "*" соответствует всем (включая слэши).


Есть реализация в исходном коде системы сборки waf. http://code.google.com/p/waf/source/browse/trunk/waflib/Node.py?r=10755#471 Может быть, это должно быть завернуто в собственную библиотеку?


Угу. Лучше всего, как уже было предложено, работать с "os.walk". Или напишите обертки вокруг globfnmatch ', возможно.


os.walk - ваш лучший выбор для этого. Я сделал пример ниже с .svn, потому что у меня было это удобно, и он отлично работал:

import re
for (dirpath, dirnames, filenames) in os.walk("."):
 if re.search(r'\.svn$', dirpath):
 for file in filenames:
 print file

licensed under cc by-sa 3.0 with attribution.