os.walk sin buscar en los directorios siguientes

103

¿Cómo me limito os.walka devolver solo archivos en el directorio que proporciono?

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
fuente
2
Otro caso en el que la multitud de enfoques posibles y todas las advertencias que los acompañan sugiere que esta funcionalidad debería agregarse a la biblioteca estándar de Python.
antred
files_with_full_path = [f.path for f in os.scandir(dir) if f.is_file()]. En caso de que solo necesite los nombres de archivo, use en f.namelugar de f.path. Esta es la solución más rápida y mucho más rápida que cualquier otra walko listdir, consulte stackoverflow.com/a/40347279/2441026 .
user136036

Respuestas:

105

Usa la walklevelfunción.

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 igual os.walk, pero puede pasarle un levelparámetro que indique qué tan profunda será la recursividad.

nosklo
fuente
3
¿Esta función realmente "recorre" toda la estructura y luego elimina las entradas por debajo de cierto punto? ¿O está pasando algo más inteligente? Ni siquiera estoy seguro de cómo verificar esto con el código.
mathtick
1
@mathtick: cuando se encuentra algún directorio en o por debajo del nivel deseado, todos sus subdirectorios se eliminan de la lista de subdirectorios para buscar a continuación. Para que no sean "paseados".
nosklo
2
Acabo de hacer +1 en esto porque estaba luchando con cómo "eliminar" directorios. Lo había intentado dirs = []y dirs = Noneeso no funcionó. map(dirs.remove, dirs)funcionó, pero con algunos mensajes "[Ninguno]" no deseados impresos. Entonces, ¿por qué del dirs[:]específicamente?
Zach Young
4
Tenga en cuenta que esto no funciona cuando se usa topdown=Falseen os.walk. Consulte el cuarto párrafo en los 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 = []y dirs = Noneno funcionarán porque simplemente crean un nuevo objeto no relacionado y lo asignan al nombre dirs. El objeto de la lista original debe modificarse in situ, no el nombre dirs.
nosklo
206

No use os.walk.

Ejemplo:

import os

root = "C:\\"
for item in os.listdir(root):
    if os.path.isfile(os.path.join(root, item)):
        print item
Yuval Adam
fuente
1
@ 576i: esto no diferencia entre archivos y directorios
4
@Alexandr os.path.isfiley os.path.isdirte permite diferenciarte. No lo entiendo, ya que os.path.isfileestá en el código de muestra desde '08 y su comentario es de '16. Esta es claramente la mejor respuesta, ya que no tiene la intención de recorrer un directorio, sino de enumerarlo.
Daniel F
@DanielF, lo que quise decir aquí es que necesita recorrer todos los elementos, mientras walkle brinda inmediatamente las listas separadas de directorios y archivos.
Ah, vale. En realidad, la respuesta de Alex parece ser mejor (usando .next()) y está mucho más cerca de tu idea.
Daniel F
Python 3.5 tiene una os.scandirfunción que permite una interacción archivo-o-directorio-objeto más sofisticada. Vea mi respuesta a continuación
adscriptor
48

Creo que la solución es realmente muy sencilla.

utilizar

break

para hacer solo la primera iteración del ciclo for, debe haber una forma más elegante.

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

La primera vez que llama a os.walk, devuelve tulipanes para el directorio actual, luego, en el siguiente ciclo, el contenido del siguiente directorio.

Tome el guión original y agregue un descanso .

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
fuente
9
Esta debería haber sido la respuesta aceptada. Simplemente agregando un "descanso" después del ciclo "for f en archivos" detiene la recursividad. También es posible que desee asegurarse de que topdown = True.
Alecz
23

La sugerencia de uso listdires buena. La respuesta directa a su pregunta en Python 2 es root, dirs, files = os.walk(dir_name).next().

La sintaxis equivalente de Python 3 es root, dirs, files = next(os.walk(dir_name))

Alex Coventry
fuente
1
Oh, estaba obteniendo todo tipo de error gracioso de ese. ValueError: demasiados valores para descomprimir
Setori
1
¡Agradable! Sin embargo, se siente como un truco. Como cuando enciendes un motor pero solo lo dejas hacer una revolución y luego presionas la llave para dejarlo morir.
Daniel F
Tropecé con esto; root, dirs, files = os.walk(dir_name).next()me daAttributeError: 'generator' object has no attribute 'next'
Evan
3
@Evan, probablemente porque es de 2008 y usa la sintaxis de Python 2. En Python 3 puedes escribir root, dirs, files = next(os.walk(dir_name))y luego las variables root, dirs, filessolo corresponderán a las variables del generador a dir_namenivel.
CervEd
13

Puede usar os.listdir()que devuelve una lista de nombres (tanto para archivos como para directorios) en un directorio determinado. Si necesita distinguir entre archivos y directorios, llame os.stat()a cada nombre.

Greg Hewgill
fuente
9

Si tiene requisitos más complejos que solo el directorio superior (por ejemplo, ignore los directorios VCS, etc.), también puede modificar la lista de directorios para evitar que os.walk vuelva a recorrerlos.

es decir:

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()

Nota: tenga cuidado de mutar la lista, en lugar de simplemente volver a enlazarla. Obviamente, os.walk no conoce la reencuadernación externa.

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

La misma idea con listdir, pero más breve:

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

Tenía ganas de tirar mis 2 peniques.

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

En Python 3, pude hacer esto:

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
fuente
Esto también funciona para Python 2. ¿Cómo llegar al segundo nivel?
2

Desde Python 3.5 puede usar en os.scandirlugar de os.listdir. En lugar de cadenas, obtienes un iterador de DirEntryobjetos a cambio. De los documentos:

Usar en scandir()lugar de listdir()puede aumentar significativamente el rendimiento del código que también necesita información sobre el tipo de archivo o los atributos del archivo, porque los DirEntryobjetos exponen esta información si el sistema operativo la proporciona al escanear un directorio. Todos los DirEntrymétodos pueden realizar una llamada al sistema, pero is_dir(), y is_file()por lo general sólo requiere una llamada al sistema para los enlaces simbólicos; DirEntry.stat()siempre requiere una llamada al sistema en Unix pero solo requiere una para enlaces simbólicos en Windows.

Puede acceder al nombre del objeto a través del DirEntry.namecual es equivalente a la salida deos.listdir

adscriptor
fuente
1
No solo "puede" usar, debe usar scandir(), ya que es mucho más rápido que listdir(). Consulte los puntos de referencia aquí: stackoverflow.com/a/40347279/2441026 .
user136036
1

También puede hacer lo siguiente:

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
fuente
2
¿No recorrerá todos los subdirectorios y archivos innecesariamente?
Pieter
0

Así es como lo resolví

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

...
Divinizado
fuente
0

Hay un problema al usar listdir. Os.path.isdir (identificador) debe ser una ruta absoluta. Para elegir subdirectorios debe:

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

La alternativa es cambiar al directorio para realizar la prueba sin os.path.join ().

Kemin Zhou
fuente
0

Puedes usar este fragmento

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

cree una lista de exclusiones, use fnmatch para omitir la estructura del directorio y realice el proceso

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
            ....

lo mismo que para 'incluye':

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

¿Por qué no simplemente usar una rangey os.walkcombinado con el zip? No es la mejor solución, pero también funcionaría.

Por ejemplo así:

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

Funciona para mí en Python 3.

Además: A también breakes más simple por cierto. (Mira la respuesta de @Pieter)

PiMathCLanguage
fuente
0

Un ligero cambio en la respuesta de Alex, pero usando __next__():

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

con el [2]ser el filede root, dirs, filemencionado en otras respuestas

Oleg
fuente
0

La carpeta raíz cambia para cada directorio que encuentra os.walk. Solucioné eso comprobando si root == directorio

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
fuente
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
fuente
1
Hola Rich, ¡bienvenido a Stack Overflow! Gracias por este fragmento de código, que puede proporcionar una ayuda limitada a corto plazo. Una explicación adecuada mejoraría enormemente su valor a largo plazo al mostrar por qué es una buena solución al problema y lo haría más útil para futuros lectores con otras preguntas similares. Por favor, editar su respuesta a añadir un poco de explicación, incluyendo los supuestos realizados.
kenny_k