Iterando a través de directorios con Python

158

Necesito recorrer los subdirectorios de un directorio determinado y buscar archivos. Si obtengo un archivo, tengo que abrirlo, cambiar el contenido y reemplazarlo con mis propias líneas.

Intenté esto:

import os

rootdir ='C:/Users/sid/Desktop/test'

for subdir, dirs, files in os.walk(rootdir):
    for file in files:
        f=open(file,'r')
        lines=f.readlines()
        f.close()
        f=open(file,'w')
        for line in lines:
            newline = "No you are not"
            f.write(newline)
        f.close()

pero recibo un error ¿Qué estoy haciendo mal?

Lobo
fuente
13
"Un error": ¿algún error en particular?
Daniel Roseman
1
Por favor, ¿podría explicar un poco sobre lo que espera hacer con los archivos / directorios una vez que los revise y trabaje según lo previsto? También proporcione detalles de error.
ChrisProsser
1
El mensaje de error que estoy recibiendo es que no se encuentra el archivo cool.txt. En mi carpeta de prueba tengo otra carpeta llamada src y en la carpeta src tengo otra carpeta llamada main, en esta carpeta tengo cool.txt
Wolf
44
¿puedes escribir el error en la pregunta? es más que molesto e innecesario tener que leer los comentarios para encontrarlo.
Charlie Parker
1
más de un año después, ¿no puedo creer que haya vuelto a solicitar que se publique el error? @Wolf
Charlie Parker

Respuestas:

301

El recorrido real a través de los directorios funciona tal como lo ha codificado. Si reemplaza el contenido del bucle interno con una printdeclaración simple , puede ver que se encuentra cada archivo:

import os
rootdir = 'C:/Users/sid/Desktop/test'

for subdir, dirs, files in os.walk(rootdir):
    for file in files:
        print os.path.join(subdir, file)

Si aún recibe errores al ejecutar lo anterior, proporcione el mensaje de error.


Actualizado para Python3

import os
rootdir = 'C:/Users/sid/Desktop/test'

for subdir, dirs, files in os.walk(rootdir):
    for file in files:
        print(os.path.join(subdir, file))
ChrisProsser
fuente
1
C: / Users / sid / Desktop / test \ src \ app / cool.txt C: / Users / sid / Desktop / test \ src \ app / woohoo.txt Ya en la declaración abierta de mi código, creo que tengo que dar la ruta absoluta al archivo. import os rootdir = 'C: / Users / spemmara / Desktop / test / src / app /' para subdir, directorios, archivos en os.walk (rootdir): para archivo en archivos: f = abierto (subdir + '/' + archivo , 'r') líneas = f.readlines () f.close () f = abierto (subdir + '/' + archivo, 'w') para línea en líneas: newline = "hey i know" f.write (newline) f.close () Gracias hombre. Está resuelto
Wolf
3
¡Hola! Tenga en cuenta que la "impresión" en python 3 requiere paréntesis, de lo contrario, devuelve un error de sintaxis. ¡Espero que esto ayude!
Tommaso Di Noto
14

Otra forma de devolver todos los archivos en subdirectorios es usar el pathlibmódulo , introducido en Python 3.4, que proporciona un enfoque orientado a objetos para manejar las rutas del sistema de archivos (Pathlib también está disponible en Python 2.7 a través del módulo pathlib2 en PyPi ):

from pathlib import Path

rootdir = Path('C:/Users/sid/Desktop/test')
# Return a list of regular files only, not directories
file_list = [f for f in rootdir.glob('**/*') if f.is_file()]

# For absolute paths instead of relative the current dir
file_list = [f for f in rootdir.resolve().glob('**/*') if f.is_file()]

Desde Python 3.5, el globmódulo también admite la búsqueda recursiva de archivos:

import os
from glob import iglob

rootdir_glob = 'C:/Users/sid/Desktop/test/**/*' # Note the added asterisks
# This will return absolute paths
file_list = [f for f in iglob('**/*', recursive=True) if os.path.isfile(f)]

El file_listde cualquiera de los enfoques anteriores se puede repetir sin la necesidad de un bucle anidado:

for f in file_list:
    print(f) # Replace with desired operations
joelostblom
fuente
1
¿Qué es preferible aquí para Python 3.6?
PhoenixDev
@ PhoenixDev No he oído hablar de un enfoque recomendado sobre el otro en general. Prefiero usarme a pathlibmí mismo, principalmente porque me gusta la sintaxis de métodos orientados a objetos. Existen otras diferencias, como la biblioteca de rutas que devuelve clases de ruta específicas en lugar de cadenas, y las funciones disponibles difieren entre las bibliotecas (por ejemplo, os.path.expanduser('~')vs Path.home()). Examine la documentación y vea qué enfoque prefiere.
joelostblom
En lugar de agregar **el patrón global, puede usar rglob.
Georgy
12

A partir de 2020 , glob.iglob(path/**, recursive=True)parece la solución más pitónica , es decir:

import glob, os

for filename in glob.iglob('/pardadox-music/**', recursive=True):
    if os.path.isfile(filename): # filter dirs
        print(filename)

Salida:

/pardadox-music/modules/her1.mod
/pardadox-music/modules/her2.mod
...

Notas:
1 - glob.iglob

glob.iglob(pathname, recursive=False)

Devuelve un iterador que produce los mismos valores que glob()sin almacenarlos todos simultáneamente.

2 - Si es recursivo True, el patrón '**'coincidirá con cualquier archivo y cero o más directoriesy subdirectories.

3 - Si el directorio contiene archivos que comienzan con  .ellos, no coincidirán de manera predeterminada. Por ejemplo, considere un directorio que contiene  card.gif y .card.gif:

>>> import glob
>>> glob.glob('*.gif') ['card.gif'] 
>>> glob.glob('.c*')['.card.gif']

4 - También puede usar rglob(pattern), que es lo mismo que llamar  glob() con **/agregado en frente del patrón relativo dado.

CONvid19
fuente
1
Esta solución pitónica no enumera los archivos ocultos (también conocidos como archivos de puntos) mientras que el aceptado sí lo hace.
ashrasmun
@ashrasmun Lo que mencionas está bien explicado en docs.python.org/3/library/glob.html
CONvid19