¿Cómo puedo buscar subcarpetas usando el módulo glob.glob?

107

Quiero abrir una serie de subcarpetas en una carpeta y buscar algunos archivos de texto e imprimir algunas líneas de los archivos de texto. Estoy usando esto:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')

Pero esto tampoco puede acceder a las subcarpetas. ¿Alguien sabe cómo puedo usar el mismo comando para acceder a las subcarpetas también?

UserYmY
fuente

Respuestas:

163

En Python 3.5 y versiones posteriores, use la nueva **/funcionalidad recursiva :

configfiles = glob.glob('C:/Users/sam/Desktop/file1/**/*.txt', recursive=True)

Cuando recursivese establece, **seguido de un separador de ruta coincide con 0 o más subdirectorios.

En versiones anteriores de Python, glob.glob()no se pueden enumerar archivos en subdirectorios de forma recursiva.

En ese caso, usaría os.walk()combinado con en su fnmatch.filter()lugar:

import os
import fnmatch

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in fnmatch.filter(files, '*.txt')]

Esto recorrerá sus directorios de forma recursiva y devolverá todos los nombres de ruta absolutos a los .txtarchivos coincidentes . En este caso específico,fnmatch.filter() puede ser excesivo, también puede usar una .endswith()prueba:

import os

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in files if f.endswith('.txt')]
Martijn Pieters
fuente
3
Puedo ver: glob.glob ('/ ruta al directorio / * / *. Txt ") funcionando para mí. Esto es básicamente usando la regla de shell de Unix.
Surya
7
@ User123: eso no enumera directorios de forma recursiva . Está enumerando todos los archivos de texto en un nivel de profundidad , pero no en otros subdirectorios o incluso directamente en path to directory.
Martijn Pieters
1
Esto no está completamente relacionado, pero ¿por qué la configuración recursive=Falsejunto con la **/ funcionalidad no proporciona la lista de archivos solo en la carpeta dada, sino en sus hijos?
Dr_Zaszuś
@ Dr_Zaszuś: ¿lo siento? **/da una lista de nombres de directorio en el directorio de trabajo actual, porque el patrón termina en /, y con recursive=Falseusted básicamente tiene un doble *, coincidiendo exactamente igual que */, pero menos eficiente.
Martijn Pieters
@ Dr_Zaszuś: utilícelo */*si necesita todos los archivos en todos los subdirectorios.
Martijn Pieters
22

Para buscar archivos en subdirectorios inmediatos:

configfiles = glob.glob(r'C:\Users\sam\Desktop\*\*.txt')

Para una versión recursiva que atraviese todos los subdirectorios, puede usar **y pasar recursive=True desde Python 3.5 :

configfiles = glob.glob(r'C:\Users\sam\Desktop\**\*.txt', recursive=True)

Ambas llamadas a funciones devuelven listas. Puede utilizar glob.iglob()para devolver los caminos uno por uno. O usepathlib :

from pathlib import Path

path = Path(r'C:\Users\sam\Desktop')
txt_files_only_subdirs = path.glob('*/*.txt')
txt_files_all_recursively = path.rglob('*.txt') # including the current dir

Ambos métodos devuelven iteradores (puede obtener rutas una por una).

jfs
fuente
Sí, lo entendí; pero tampoco esperaba glob()admitir patrones en directorios.
Martijn Pieters
Comentario eliminado, veo ahora que dio una impresión equivocada; además, el parche incluye una actualización de la documentación para el **caso de recursividad. Sin embargo, para **que el trabajo, usted tiene que ajustar el recursion=Trueinterruptor, por cierto.
Martijn Pieters
20

Hay mucha confusión sobre este tema. Déjame ver si puedo aclararlo (Python 3.7):

  1. glob.glob('*.txt') :coincide con todos los archivos que terminan en '.txt' en el directorio actual
  2. glob.glob('*/*.txt') :igual que 1
  3. glob.glob('**/*.txt') :coincide con todos los archivos que terminan en '.txt' solo en los subdirectorios inmediatos , pero no en el directorio actual
  4. glob.glob('*.txt',recursive=True) :igual que 1
  5. glob.glob('*/*.txt',recursive=True) :igual que 3
  6. glob.glob('**/*.txt',recursive=True):coincide con todos los archivos que terminan en '.txt' en el directorio actual y en todos los subdirectorios

Así que es mejor especificar siempre recursive=True.

germen
fuente
1
¡Esta debería ser la mejor respuesta!
Abhik Sarkar
17

El paquete glob2 admite comodines y es razonablemente rápido

code = '''
import glob2
glob2.glob("files/*/**")
'''
timeit.timeit(code, number=1)

En mi computadora portátil, se necesitan aproximadamente 2 segundos para hacer coincidir > 60,000 rutas de archivo .

megawac
fuente
9

Puedes usar Formic con Python 2.6

import formic
fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")

Divulgación: soy el autor de este paquete.

Andrew Alcock
fuente
4

Aquí hay una versión adaptada que habilita la glob.globmisma funcionalidad sin usar glob2.

def find_files(directory, pattern='*'):
    if not os.path.exists(directory):
        raise ValueError("Directory not found {}".format(directory))

    matches = []
    for root, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            full_path = os.path.join(root, filename)
            if fnmatch.filter([full_path], pattern):
                matches.append(os.path.join(root, filename))
    return matches

Entonces, si tiene la siguiente estructura de directorios

tests/files
├── a0
   ├── a0.txt
   ├── a0.yaml
   └── b0
       ├── b0.yaml
       └── b00.yaml
└── a1

Puedes hacer algo como esto

files = utils.find_files('tests/files','**/b0/b*.yaml')
> ['tests/files/a0/b0/b0.yaml', 'tests/files/a0/b0/b00.yaml']

Prácticamente el fnmatchpatrón coincide con todo el nombre del archivo, en lugar de solo el nombre del archivo.

cevaris
fuente
2

configfiles = glob.glob('C:/Users/sam/Desktop/**/*.txt")

No funciona para todos los casos, en su lugar use glob2

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")
NILESH KUMAR
fuente
2

Si puede instalar el paquete glob2 ...

import glob2
filenames = glob2.glob("C:\\top_directory\\**\\*.ext")  # Where ext is a specific file extension
folders = glob2.glob("C:\\top_directory\\**\\")

Todos los nombres de archivo y carpetas:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  
soñar
fuente
2

Si está ejecutando Python 3.4+, puede usar el pathlibmódulo. El Path.glob()método admite el **patrón, que significa "este directorio y todos los subdirectorios, de forma recursiva". Devuelve un generador que produce Pathobjetos para todos los archivos coincidentes.

from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")
Eugene Yarmash
fuente
0

Como señaló Martijn, glob solo puede hacer esto a través del **operador introducido en Python 3.5. Dado que el OP solicitó explícitamente el módulo glob, lo siguiente devolverá un iterador de evaluación perezoso que se comporta de manera similar

import os, glob, itertools

configfiles = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.txt'))
                         for root, dirs, files in os.walk('C:/Users/sam/Desktop/file1/'))

Sin configfilesembargo, tenga en cuenta que solo puede iterar una vez en este enfoque. Si necesita una lista real de archivos de configuración que se puedan usar en múltiples operaciones, tendrá que crearla explícitamente usando list(configfiles).

f0xdx
fuente
0

El comando rglobhará una recursión infinita hasta el subnivel más profundo de la estructura de su directorio. Sin embargo, si solo desea un nivel de profundidad, no lo use.

Me doy cuenta de que el OP estaba hablando de usar glob.glob. Sin embargo, creo que esto responde a la intención, que es buscar en todas las subcarpetas de forma recursiva.

La rglobfunción producido recientemente un aumento de 100x en la velocidad para un algoritmo de procesamiento de datos que se utiliza la estructura de carpetas como supuesto fijado para la orden de los datos de lectura. Sin embargo, rglobpudimos hacer un solo escaneo una vez a través de todos los archivos en o debajo de un directorio principal específico, guardar sus nombres en una lista (más de un millón de archivos), luego usar esa lista para determinar qué archivos necesitábamos abrir en cualquier apuntar en el futuro según las convenciones de nomenclatura de archivos solo frente a la carpeta en la que se encontraban.

brethvoice
fuente