Como você implementar patternsets de estilo formiga em python para selecionar grupos de arquivos?

StackOverflow https://stackoverflow.com/questions/161755

  •  03-07-2019
  •  | 
  •  

Pergunta

Ant tem uma boa maneira de selecionar grupos de arquivos, a maioria com folga utilizando ** para indicar uma árvore de diretórios. Por exemplo.

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

Mais exemplos podem ser vistos aqui:

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

Como você implementar isso em python, de modo que você poderia fazer algo como:

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

=>
CVS/Repository
mydir/mysubdir/CVS/Entries
mydir/mysubdir/foo/bar/CVS/Entries
Foi útil?

Solução

Assim que você se deparar com um **, você vai ter que recurse através de toda a estrutura de diretórios, então eu acho que neste ponto, o método mais fácil é para percorrer o diretório com os.walk, construir um caminho e, em seguida, verificar se ele corresponde ao padrão. Provavelmente, você pode converter para uma regex por algo como:

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+"$")

(Embora note que este não é que com todos os recursos - que não suporta padrões glob estilo [a-z] por exemplo, embora isso provavelmente poderia ser adicionado). (A primeira partida \*\*/ é cobrir casos como \*\*/CVS ./CVS de correspondência, bem como ter apenas \*\* para corresponder na cauda.)

No entanto, obviamente, você não quer recurse através de tudo abaixo da pasta corrente quando não estiver processando um padrão **, então eu acho que você vai precisar de uma abordagem em duas fases. Eu não tentei implementar a seguir, e há provavelmente alguns casos de canto, mas eu acho que deve funcionar:

  1. Dividir o padrão em seu seperator diretório. ie pat.split('/') -> ['**','CVS','*']

  2. Recurse através dos diretórios, e olhar para a parte relevante do padrão para este nível. ie. n levels deep -> look at pat[n].

  3. Se a chave pat[n] == '**' à estratégia acima:

    • reconstruir o padrão com dirsep.join(pat[n:])
    • converter para um regex com glob\_to\_regex()
    • Recursively os.walk através do diretório atual, construindo o caminho relativo para o nível que começou no. Se o caminho corresponde a regex, cedê-lo.
  4. Se pat não corresponde "**", e é o último elemento no padrão, em seguida, produzir todos os arquivos / diretórios correspondentes glob.glob(os.path.join(curpath,pat[n]))

  5. Se pat não corresponde "**", e não é o último elemento no padrão, em seguida, para cada diretório, verifique se ele corresponde (com glob) pat[n]. Se assim for, recurse para baixo através dele, incrementar a profundidade (por isso vai olhar para pat[n+1])

Outras dicas

Infelizmente, este é um bom tempo após a sua OP. I acabou de lançar um pacote Python que faz exatamente isso - ele é chamado fórmico e está disponível na PyPI Cheeseshop . Com fórmico, o seu problema é resolvido com:

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

Há um pequeno complexidade: default_excludes. Fórmico, assim como Ant, diretórios exclui CVS por padrão (como a maior parte coletar arquivos a partir deles para uma compilação é perigoso), a resposta padrão para a pergunta resultaria em nenhum arquivo. Definindo default_excludes = desativa falsos esse comportamento.

os.walk é seu amigo. Veja o exemplo no manual Python ( https://docs.python.org/2/library/os .html # os.walk ) e tentar construir algo a partir disso.

Para corresponder "**/CVS/*" contra um nome de arquivo que você começa, você pode fazer algo como isto:

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

Em fnmatch.fnmatch, "*" corresponde a qualquer coisa (incluindo barras).

Há uma implementação no código fonte do sistema build 'waf'. http://code.google .com / p / WAF / fonte / browse / trunk / waflib / Node.py? r = 10755 # 471 Pode ser este deve ser embrulhado em uma biblioteca própria?

Sim. Sua melhor aposta é, como já foi sugerido, para trabalhar com 'os.walk'. Ou, invólucros de gravação em torno de ' glob ' e ' fnmatch ' módulos, talvez.

os.walk é sua melhor aposta por esta. Eu fiz o exemplo abaixo com .svn porque eu tinha que prático, e funcionou muito bem:

import re

for (dirpath, dirnames, filenames) in os.walk("."):
    if re.search(r'\.svn$', dirpath):
        for file in filenames:
            print file
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top