Excluindo diretórios no os.walk


147

Estou escrevendo um script que desce em uma árvore de diretórios (usando os.walk ()) e depois visita cada arquivo que corresponde a uma determinada extensão de arquivo. No entanto, como algumas das árvores de diretórios em que minha ferramenta será usada também contêm subdiretórios que, por sua vez, contêm MUITO material inútil (para o propósito deste script), achei que adicionaria uma opção para o usuário especificar uma lista de diretórios a serem excluídos da passagem.

Isso é bastante fácil com os.walk (). Afinal, cabe a mim decidir se realmente quero visitar os respectivos arquivos / diretórios gerados por os.walk () ou simplesmente ignorá-los. O problema é que, se eu tenho, por exemplo, uma árvore de diretórios como esta:

root--
     |
     --- dirA
     |
     --- dirB
     |
     --- uselessStuff --
                       |
                       --- moreJunk
                       |
                       --- yetMoreJunk

e eu quero excluir uselessStuff e todos os seus filhos, os.walk () ainda descerá para todos os (potencialmente milhares de) subdiretórios do uselessStuff , que, escusado será dizer, diminui bastante as coisas. Em um mundo ideal, eu poderia dizer ao os.walk () que nem se desse ao trabalho de gerar mais filhos de coisas inúteis , mas, pelo que sei , não há como fazer isso (existe?).

Alguém tem alguma ideia? Talvez haja uma biblioteca de terceiros que forneça algo assim?

Respostas:


242

A modificação dirs no local removerá os arquivos e diretórios (subseqüentes) visitados por os.walk:

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    dirs[:] = [d for d in dirs if d not in exclude]

Da ajuda (os.walk):

Quando a descida é verdadeira, o chamador pode modificar a lista de nomes de diretórios no local (por exemplo, através da atribuição de del ou fatia), e o walk recursará apenas nos subdiretórios cujos nomes permanecem em nomes de diretórios; isso pode ser usado para remover a pesquisa ...


31
Por que dirs[:] =?
ben

56
@ ben: dirs[:] = valuemodifica dirs no local . Ele altera o conteúdo da lista dirssem alterar o contêiner. Como help(os.walk)mencionado, isso é necessário se você deseja afetar a maneira como os.walkpercorre os subdiretórios. ( dirs = valueapenas reatribui (ou "vincula") a variável dirsa uma nova lista, sem modificar o original dirs.)
unutbu

6
Você também pode usar filter():dirs[:] = list(filter(lambda x: not x in exclude, dirs))
NuclearPeon

2
@ p014k: Você pode escrever sua própria função geradora, que chama os.walke gera root, dirs, filesapós excluir .git(ou o que mais você desejar) dirs.
unutbu

3
@unutbu Apenas informando que, em um caso, essa otimização reduziu o tempo de deslocamento de mais de 100 segundos para cerca de 2 segundos. É o que chamo de otimização que vale a pena. : D
antred

7

... uma forma alternativa da excelente resposta do @ unutbu que lê um pouco mais diretamente, considerando que a intenção é excluir diretórios, ao custo de O (n ** 2) vs O (n).

(É necessário fazer uma cópia da lista de diretórios list(dirs)para a execução correta)

# exclude = set([...])
for root, dirs, files in os.walk(top, topdown=True):
    [dirs.remove(d) for d in list(dirs) if d in exclude]

5
Se você quiser ser mais direto ao custo de alguma memória, é melhor escrever dirs[:] = set(dirs) - exclude. Pelo menos ainda é \ $ O (n) \ $ e você não construir uma compreensão apenas por seus efeitos secundários ...
301_Moved_Permanently

3
Isso não é realmente ruim, mas não Python idiomático, na minha opinião.
Torsten Bronger

for d in list(dirs)é um pouco estranho. dirsjá é uma lista. E o que você tem não é realmente uma compreensão de lista. dirs.remove(d)não retorna nada, então você acaba com uma lista cheia de Nones. Eu concordo com @Torsten.
seanahern
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.