os.walk sem procurar nos diretórios abaixo

103

Como faço os.walkpara limitar para retornar apenas arquivos no diretório que eu forneço?

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
    return outputList
Setori
fonte
2
Outro caso em que a infinidade de abordagens possíveis e todas as advertências que as acompanham sugere que essa funcionalidade deve ser adicionada à biblioteca padrão do Python.
antred
files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]. Caso você precise apenas dos nomes dos arquivos, use em f.namevez de f.path. Essa é a solução mais rápida e muito mais rápida do que qualquer outra walkou listdir, consulte stackoverflow.com/a/40347279/2441026 .
user136036

Respostas:

105

Use a walklevelfunção.

import os

def walklevel(some_dir, level=1):
    some_dir = some_dir.rstrip(os.path.sep)
    assert os.path.isdir(some_dir)
    num_sep = some_dir.count(os.path.sep)
    for root, dirs, files in os.walk(some_dir):
        yield root, dirs, files
        num_sep_this = root.count(os.path.sep)
        if num_sep + level <= num_sep_this:
            del dirs[:]

Funciona da mesma forma os.walk, mas você pode passar um levelparâmetro que indica a profundidade da recursão.

nosklo
fonte
3
Essa função realmente "percorre" toda a estrutura e exclui as entradas abaixo de um determinado ponto? Ou algo mais inteligente está acontecendo? Eu nem tenho certeza de como verificar isso com o código. --python iniciante
mathtick
1
@mathtick: quando algum diretório no nível desejado ou abaixo dele for encontrado, todos os seus subdiretórios são removidos da lista de subdiretórios a serem pesquisados ​​em seguida. Portanto, eles não serão "percorridos".
nosklo
2
Acabei de marcar isto com +1 porque estava lutando para "excluir" diretórios. Eu já havia tentado dirs = []e dirs = Nonemas aqueles não funcionou. map(dirs.remove, dirs)funcionou, mas com algumas mensagens indesejadas '[Nenhum]' impressas. Então, por que del dirs[:]especificamente?
Zach Young,
4
Observe que isso não funciona ao usar topdown=Falseem os.walk. Veja o 4º parágrafo nos documentos :Modifying dirnames when topdown is False has no effect on the behavior of the walk, because in bottom-up mode the directories in dirnames are generated before dirpath itself is generated.
dthor
3
@ZacharyYoung dirs = []e dirs = Nonenão funcionará porque eles apenas criam um novo objeto não relacionado e atribuem ao nome dirs. O objeto de lista original precisa ser modificado no local, não o nome dirs.
nosklo
206

Não use os.walk.

Exemplo:

import os

root = "C:\\"
for item in os.listdir(root):
    if os.path.isfile(os.path.join(root, item)):
        print item
Yuval Adam
fonte
1
@ 576i: isso não diferencia entre arquivos e diretórios
4
@Alexandr os.path.isfilee os.path.isdirpermite diferenciar. Não entendi, pois os.path.isfileestá no código de amostra desde '08 e seu comentário é de '16. Esta é claramente a melhor resposta, já que você não pretende percorrer um diretório, mas listá-lo.
Daniel F
@DanielF, o que eu quis dizer aqui é que você precisa fazer um loop em todos os itens, enquanto walkfornece imediatamente as listas separadas de diretórios e arquivos.
Ah ok. Na verdade, a resposta do Alex parece ser melhor (usando .next()) e está muito mais próxima da sua ideia.
Daniel F
O Python 3.5 tem uma os.scandirfunção que permite uma interação mais sofisticada de arquivo ou diretório-objeto. Veja minha resposta abaixo
redator de
48

Acho que a solução é muito simples.

usar

break

para fazer apenas a primeira iteração do loop for, deve haver uma maneira mais elegante.

for root, dirs, files in os.walk(dir_name):
    for f in files:
        ...
        ...
    break
...

A primeira vez que você chama os.walk, ele retorna tulipas para o diretório atual e, no próximo loop, o conteúdo do próximo diretório.

Pegue o roteiro original e apenas adicione uma pausa .

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        for f in files:
            if os.path.splitext(f)[1] in whitelist:
                outputList.append(os.path.join(root, f))
            else:
                self._email_to_("ignore")
        break
    return outputList
Pieter
fonte
9
Esta deveria ter sido a resposta aceita. Simplesmente adicionar um "break" após o loop "for f in files" interrompe a recursividade. Você também pode querer ter certeza de que topdown = True.
Alecz
23

A sugestão de uso listdiré boa. A resposta direta à sua pergunta no Python 2 é root, dirs, files = os.walk(dir_name).next().

A sintaxe Python 3 equivalente é root, dirs, files = next(os.walk(dir_name))

Alex Coventry
fonte
1
Oh, eu estava recebendo todo tipo de erro engraçado com aquele. ValueError: muitos valores para desempacotar
Setori,
1
Agradável! Parece um hack, no entanto. Como quando você liga um motor, mas só o deixa dar uma volta e depois puxa a chave para deixá-lo morrer.
Daniel F
Tropecei nisso; root, dirs, files = os.walk(dir_name).next()dá-meAttributeError: 'generator' object has no attribute 'next'
Evan
3
@Evan, provavelmente porque é de 2008 e usa a sintaxe Python 2. No Python 3 você pode escrever root, dirs, files = next(os.walk(dir_name))e então as variáveis root, dirs, filescorresponderão apenas às variáveis ​​do gerador no dir_namenível.
CervEd 01 de
13

Você pode usar o os.listdir()qual retorna uma lista de nomes (para arquivos e diretórios) em um determinado diretório. Se você precisar distinguir entre arquivos e diretórios, chame os.stat()cada nome.

Greg Hewgill
fonte
9

Se você tiver requisitos mais complexos do que apenas o diretório superior (por exemplo, ignore os diretórios VCS, etc.), você também pode modificar a lista de diretórios para evitar que os.walk volte a eles.

ie:

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        dirs[:] = [d for d in dirs if is_good(d)]
        for f in files:
            do_stuff()

Observação - tome cuidado para alterar a lista, em vez de apenas religá-la. Obviamente, o os.walk não sabe sobre a religação externa.

Brian
fonte
6
for path, dirs, files in os.walk('.'):
    print path, dirs, files
    del dirs[:] # go only one level deep
masterxilo
fonte
4

A mesma ideia listdir, mas mais curta:

[f for f in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, f))]
Oleg Gryb
fonte
3

Tive vontade de jogar meus 2 centavos.

baselevel = len(rootdir.split("\\"))
for subdirs, dirs, files in os.walk(rootdir):
    curlevel = len(subdirs.split("\\"))
    if curlevel <= baselevel + 1:
        [do stuff]
Matt R
fonte
2

No Python 3, consegui fazer isso:

import os
dir = "/path/to/files/"

#List all files immediately under this folder:
print ( next( os.walk(dir) )[2] )

#List all folders immediately under this folder:
print ( next( os.walk(dir) )[1] )
Jay Sheth
fonte
Isso também funciona para Python 2. Como obter o segundo nível?
2

Desde Python 3.5, você pode usar em os.scandirvez de os.listdir. Em vez de strings, você obtém um iterador de DirEntryobjetos em retorno. Dos documentos:

Usar em scandir()vez de listdir()pode aumentar significativamente o desempenho do código que também precisa de informações de tipo de arquivo ou atributo de arquivo, porque os DirEntryobjetos expõem essas informações se o sistema operacional as fornecer durante a varredura de um diretório. Todos os DirEntrymétodos podem realizar uma chamada de sistema, mas is_dir()e is_file()normalmente requerem apenas uma chamada de sistema para links simbólicos; DirEntry.stat()sempre requer uma chamada de sistema no Unix, mas requer apenas uma para links simbólicos no Windows.

Você pode acessar o nome do objeto por meio do DirEntry.namequal é então equivalente à saída deos.listdir

ascripter
fonte
1
Não apenas "pode" você usar, você deve usar scandir(), pois é muito mais rápido do que listdir(). Veja benchmarks aqui: stackoverflow.com/a/40347279/2441026 .
user136036
1

Você também pode fazer o seguinte:

for path, subdirs, files in os.walk(dir_name):
    for name in files:
        if path == ".": #this will filter the files in the current directory
             #code here
Diana G
fonte
2
Isso não vai percorrer todos os sub-dirs e arquivos desnecessariamente?
Pieter
0

Foi assim que resolvi

if recursive:
    items = os.walk(target_directory)
else:
    items = [next(os.walk(target_directory))]

...
Deificado
fonte
0

Há um problema ao usar listdir. O os.path.isdir (identificador) deve ser um caminho absoluto. Para escolher subdiretórios, você:

for dirname in os.listdir(rootdir):
  if os.path.isdir(os.path.join(rootdir, dirname)):
     print("I got a subdirectory: %s" % dirname)

A alternativa é mudar para o diretório para fazer o teste sem o os.path.join ().

Kemin Zhou
fonte
0

Você pode usar este snippet

for root, dirs, files in os.walk(directory):
    if level > 0:
        # do some stuff
    else:
        break
    level-=1
RousseauAlexandre
fonte
0

crie uma lista de exclusões, use fnmatch para pular a estrutura de diretório e fazer o processo

excludes= ['a\*\b', 'c\d\e']
for root, directories, files in os.walk('Start_Folder'):
    if not any(fnmatch.fnmatch(nf_root, pattern) for pattern in excludes):
        for root, directories, files in os.walk(nf_root):
            ....
            do the process
            ....

o mesmo que para 'inclui':

if **any**(fnmatch.fnmatch(nf_root, pattern) for pattern in **includes**):
Hamsavardhini
fonte
0

Por que não usar simplesmente um rangee os.walkcombinado com o zip? Não é a melhor solução, mas também funcionaria.

Por exemplo, assim:

# your part before
for count, (root, dirs, files) in zip(range(0, 1), os.walk(dir_name)):
    # logic stuff
# your later part

Funciona para mim no python 3.

Além disso: A breaké mais simples, aliás. (Veja a resposta de @Pieter)

PiMathCLanguage
fonte
0

Uma ligeira mudança na resposta de Alex, mas usando __next__():

print(next(os.walk('d:/'))[2]) ou print(os.walk('d:/').__next__()[2])

com a [2]ser o fileno root, dirs, filemencionado em outras respostas

Oleg
fonte
0

A pasta raiz muda para cada diretório que os.walk encontrar. Eu resolvo isso verificando se root == diretório

def _dir_list(self, dir_name, whitelist):
    outputList = []
    for root, dirs, files in os.walk(dir_name):
        if root == dir_name: #This only meet parent folder
            for f in files:
                if os.path.splitext(f)[1] in whitelist:
                    outputList.append(os.path.join(root, f))
                else:
                    self._email_to_("ignore")
    return outputList
Pedro J. Sola
fonte
0
import os

def listFiles(self, dir_name):
    names = []
    for root, directory, files in os.walk(dir_name):
        if root == dir_name:
            for name in files:
                names.append(name)
    return names
Rico
fonte
1
Olá, Rich, bem-vindo ao Stack Overflow! Obrigado por este trecho de código, que pode fornecer alguma ajuda limitada de curto prazo. Uma explicação adequada melhoraria muito seu valor a longo prazo, mostrando por que essa é uma boa solução para o problema, e a tornaria mais útil para leitores futuros com outras questões semelhantes. Por favor edite sua resposta para adicionar alguma explicação, incluindo as suposições que você fez.
kenny_k