¿Cómo usar para encontrar archivos recursivamente?

6 minutos de lectura

Avatar de usuario de Ben Gartner
Ben Gartner

Me gustaría enumerar todos los archivos recursivamente en un directorio. Actualmente tengo una estructura de directorios como esta:

  • src/main.c
  • src/dir/file1.c
  • src/another-dir/file2.c
  • src/another-dir/nested/files/file3.c

He intentado hacer lo siguiente:

from glob import glob

glob(os.path.join('src','*.c'))

Pero esto solo obtendrá archivos directamente en el src subcarpeta, por ejemplo, obtengo main.c pero no voy a conseguir file1.c, file2.c etc.

from glob import glob

glob(os.path.join('src','*.c'))
glob(os.path.join('src','*','*.c'))
glob(os.path.join('src','*','*','*.c'))
glob(os.path.join('src','*','*','*','*.c'))

Pero esto es obviamente limitado y torpe, ¿cómo puedo hacerlo correctamente?

  • no glob('src/**/*.c') funciona en este caso?

    –Likith Reddy

    13 de septiembre de 2021 a las 8:06

Avatar de usuario de Johan Dahlin
johan dahlin

Hay un par de maneras:

pathlib.Path().rglob()

Usar pathlib.Path().rglob() desde el pathlib módulo, que se introdujo en Python 3.5.

from pathlib import Path

for path in Path('src').rglob('*.c'):
    print(path.name)

globo.glob()

Si no quieres usar pathlib, usa glob.glob():

from glob import glob

for filename in glob('src/**/*.c', recursive=True):
    print(filename)   

Para los casos en los que los archivos coincidentes comienzan con un punto (.); como archivos en el directorio actual o archivos ocultos en un sistema basado en Unix, use el os.walk() solución a continuación.

os.caminar()

Para versiones anteriores de Python, use os.walk() para recorrer recursivamente un directorio y fnmatch.filter() para hacer coincidir con una expresión simple:

import fnmatch
import os

matches = []
for root, dirnames, filenames in os.walk('src'):
    for filename in fnmatch.filter(filenames, '*.c'):
        matches.append(os.path.join(root, filename))

Esta versión también debería ser más rápida según la cantidad de archivos que tenga, ya que el módulo pathlib tiene un poco de sobrecarga. os.walk().

  • Para Python anterior a 2.2 hay os.path.walk() que es un poco más complicado de usar que os.walk()

    – John LaRooy

    2 de febrero de 2010 a las 19:34

  • @gnibbler Sé que es un comentario antiguo, pero mi comentario es solo para que la gente sepa que os.path.walk() está en desuso y se ha eliminado en Python 3.

    – Pedro Cunha

    18 de enero de 2013 a las 16:14

  • @DevC que podría funcionar en el caso específico planteado en esta pregunta, pero es fácil imaginar a alguien que quiera usarlo con consultas como ‘a*.c’, etc., por lo que creo que vale la pena mantener la respuesta actual algo lenta.

    – Johan Dahlin

    19 mayo 2014 a las 19:29

  • Por lo que vale, en mi caso, encontrar más de 10,000 archivos con glob fue mucho más lento que con os.walk, así que opté por la última solución por ese motivo.

    – Godsmith

    12 de septiembre de 2018 a las 6:23

  • Para pitón 3.4, pathlib.Path('src').glob('**/*.c') Deberia trabajar.

    – CivFan

    11 de abril de 2019 a las 0:16

Avatar de usuario de Pedro Lobito
Pedro Lobito

Para pitón >= 3.5 puedes usar **, recursive=Truees decir:

import glob
for f in glob.glob('/path/**/*.c', recursive=True):
    print(f)

Si es recursivo True (por defecto False), el patrón ** coincidirá con cualquier archivo y cero o más directories y subdirectories. Si el patrón es seguido por un os.sepsolo directorios y subdirectories fósforo.


Demostración de Python 3

  • Esto funciona mejor que pathlib.Path(‘./path/’).glob(‘*/‘) porque también lo está en una carpeta con un tamaño de 0

    – Charles Walker

    18 de abril de 2020 a las 15:14

  • En Python 3.9.1, recursivo se establece en Falso de forma predeterminada.

    – PYB

    30 de diciembre de 2020 a las 21:53

  • recursive también está configurado para False por defecto en Python 3.8.*.

    – rayryeng

    25 oct 2021 a las 14:01

Avatar de usuario de Bruno Oliveira
Bruno Oliveira

Similar a otras soluciones, pero usando fnmatch.fnmatch en lugar de glob, ya que os.walk ya enumeró los nombres de archivo:

import os, fnmatch


def find_files(directory, pattern):
    for root, dirs, files in os.walk(directory):
        for basename in files:
            if fnmatch.fnmatch(basename, pattern):
                filename = os.path.join(root, basename)
                yield filename


for filename in find_files('src', '*.c'):
    print 'Found C source:', filename

Además, el uso de un generador le permite procesar cada archivo a medida que se encuentra, en lugar de buscar todos los archivos. y luego procesándolos.

Avatar de usuario de Miracle2k
milagro2k

Modifiqué el módulo glob para admitir ** para globbing recursivo, por ejemplo:

>>> import glob2
>>> all_header_files = glob2.glob('src/**/*.c')

https://github.com/miracle2k/python-glob2/

Útil cuando desea proporcionar a sus usuarios la capacidad de usar la sintaxis ** y, por lo tanto, os.walk() por sí solo no es lo suficientemente bueno.

avatar de usuario de taleinat
taleinat

Comenzando con Python 3.4, uno puede usar el glob() método de uno de los Path clases en el nuevo rutalib módulo, que soporta ** comodines. Por ejemplo:

from pathlib import Path

for file_path in Path('src').glob('**/*.c'):
    print(file_path) # do whatever you need with these files

Actualizar:
A partir de Python 3.5, la misma sintaxis también es compatible con glob.glob().

Avatar de usuario de Sebastian Mach
Sebastián Mach

import os
import fnmatch


def recursive_glob(treeroot, pattern):
    results = []
    for base, dirs, files in os.walk(treeroot):
        goodfiles = fnmatch.filter(files, pattern)
        results.extend(os.path.join(base, f) for f in goodfiles)
    return results

fnmatch te da exactamente los mismos patrones que globpor lo que este es realmente un excelente reemplazo para glob.glob con una semántica muy cercana. Una versión iterativa (por ejemplo, un generador), IOW un reemplazo para glob.iglobes una adaptación trivial (solo yield los resultados intermedios sobre la marcha, en lugar de extending una sola lista de resultados para volver al final).

Avatar de usuario de Geoff Reedy
Geoff Reedy

Querrás usar os.walk para recopilar nombres de archivo que coincidan con sus criterios. Por ejemplo:

import os
cfiles = []
for root, dirs, files in os.walk('src'):
  for file in files:
    if file.endswith('.c'):
      cfiles.append(os.path.join(root, file))

¿Ha sido útil esta solución?