Come posso cercare nelle sottocartelle utilizzando il modulo glob.glob?


107

Voglio aprire una serie di sottocartelle in una cartella e trovare alcuni file di testo e stampare alcune righe dei file di testo. Sto usando questo:

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

Ma anche questo non può accedere alle sottocartelle. Qualcuno sa come posso usare lo stesso comando per accedere anche alle sottocartelle?


Risposte:


163

In Python 3.5 e versioni successive utilizzare il nuovo ricorsivo **/ funzionalità :

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

Quando recursiveè impostato, **seguito da un separatore di percorso corrisponde a 0 o più sottodirectory.

Nelle versioni precedenti di Python, glob.glob()non era possibile elencare i file nelle sottodirectory in modo ricorsivo.

In tal caso userei in os.walk()combinazione con fnmatch.filter()invece:

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')]

Questo percorrerà le directory in modo ricorsivo e restituirà tutti i nomi di percorso assoluti ai .txtfile corrispondenti . In questo caso specificofnmatch.filter() potrebbe essere eccessivo, potresti anche usare un .endswith()test:

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')]

3
Riesco a vedere: glob.glob ('/ path to directory / * / *. Txt ") funziona per me. Questo è fondamentalmente usando la regola della shell Unix.
Surya

7
@ User123: che non elenca le directory in modo ricorsivo . Stai elencando tutti i file di testo a un livello di profondità , ma non in ulteriori sottodirectory o anche direttamente in path to directory.
Martijn Pieters

1
Questo non è completamente correlato, ma perché l'impostazione recursive=Falseinsieme alla **/ funzionalità non fornisce l'elenco dei file solo nella cartella data, ma piuttosto nei suoi figli?
Dr_Zaszuś

@ Dr_Zaszuś: scusa? **/fornisce un elenco di nomi di directory nella directory di lavoro corrente, perché il modello termina con /, e con recursive=Falsete fondamentalmente hai un doppio *, che corrisponde esattamente allo stesso modo di */, solo meno efficiente.
Martijn Pieters

@ Dr_Zaszuś: usa */*se hai bisogno di tutti i file in tutte le sottodirectory.
Martijn Pieters

22

Per trovare i file nelle sottodirectory immediate:

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

Per una versione ricorsiva che attraversa tutte le sottodirectory, puoi usare **e passare recursive=True da Python 3.5 :

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

Entrambe le chiamate di funzione restituiscono elenchi. È possibile utilizzare glob.iglob()per restituire i percorsi uno per uno. Oppure usapathlib :

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

Entrambi i metodi restituiscono iteratori (puoi ottenere i percorsi uno per uno).


Sì, l'ho capito; ma non mi aspettavo nemmeno glob()di supportare i pattern nelle directory.
Martijn Pieters

Commento cancellato, vedo ora che dava un'impressione sbagliata; inoltre, la patch include un aggiornamento della documentazione per il **caso di ricorsione. Ma per **il lavoro, è necessario impostare l' recursion=Trueinterruttore, btw.
Martijn Pieters

20

C'è molta confusione su questo argomento. Fammi vedere se riesco a chiarirlo (Python 3.7):

  1. glob.glob('*.txt') :corrisponde a tutti i file che terminano con ".txt" nella directory corrente
  2. glob.glob('*/*.txt') :uguale a 1
  3. glob.glob('**/*.txt') :corrisponde a tutti i file che terminano con ".txt" solo nelle sottodirectory immediate , ma non nella directory corrente
  4. glob.glob('*.txt',recursive=True) :uguale a 1
  5. glob.glob('*/*.txt',recursive=True) :uguale a 3
  6. glob.glob('**/*.txt',recursive=True):corrisponde a tutti i file che terminano con ".txt" nella directory corrente e in tutte le sottodirectory

Quindi è meglio specificare sempre recursive=True.


1
Questa dovrebbe essere la risposta migliore!
Abhik Sarkar,

17

Il pacchetto glob2 supporta i caratteri jolly ed è ragionevolmente veloce

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

Sul mio laptop ci vogliono circa 2 secondi per abbinare > 60.000 percorsi di file .


9

Puoi usare Formic con Python 2.6

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

Divulgazione - Sono l'autore di questo pacchetto.


4

Ecco una versione adattata che consente glob.globfunzionalità simili senza utilizzare 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

Quindi, se hai la seguente struttura dir

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

Puoi fare qualcosa del genere

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

Più o meno la fnmatchcorrispondenza del modello sull'intero nome del file stesso, piuttosto che solo sul nome del file.


2

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

Non funziona per tutti i casi, usa invece glob2

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

2

Se puoi installare il pacchetto glob2 ...

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

Tutti i nomi di file e le cartelle:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  

2

Se stai utilizzando Python 3.4+, puoi usare il pathlibmodulo. Il Path.glob()metodo supporta il **pattern, che significa "questa directory e tutte le sottodirectory, in modo ricorsivo". Restituisce un generatore che produce Pathoggetti per tutti i file corrispondenti.

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

0

Come sottolineato da Martijn, glob può farlo solo tramite l' **operatore introdotto in Python 3.5. Poiché l'OP ha richiesto esplicitamente il modulo glob, quanto segue restituirà un iteratore di valutazione pigro che si comporta in modo simile

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/'))

Nota che puoi ripetere solo una volta configfilesin questo approccio. Se hai bisogno di un vero elenco di file di configurazione che possono essere utilizzati in più operazioni, dovresti crearlo esplicitamente usando list(configfiles).


0

Il comando rglobeseguirà una ricorsione infinita nel livello inferiore più profondo della struttura della directory. Se vuoi solo un livello profondo, non usarlo, tuttavia.

Mi rendo conto che l'OP stava parlando dell'utilizzo di glob.glob. Credo che questo risponda all'intento, tuttavia, che è quello di cercare in tutte le sottocartelle in modo ricorsivo.

La rglobfunzione ha recentemente prodotto un aumento di 100 volte della velocità per un algoritmo di elaborazione dati che utilizzava la struttura delle cartelle come presupposto fisso per l'ordine di lettura dei dati. Tuttavia, rglobsiamo stati in grado di eseguire una singola scansione una volta su tutti i file in o al di sotto di una directory principale specificata, salvare i loro nomi in un elenco (oltre un milione di file), quindi utilizzare quell'elenco per determinare quali file dovevamo aprire in qualsiasi punto nel futuro in base alle convenzioni di denominazione dei file solo rispetto alla cartella in cui si trovavano.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.