Comment puis-je rechercher des sous-dossiers à l'aide du module glob.glob?

107

Je veux ouvrir une série de sous-dossiers dans un dossier et trouver des fichiers texte et imprimer quelques lignes des fichiers texte. J'utilise ceci:

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

Mais cela ne peut pas non plus accéder aux sous-dossiers. Quelqu'un sait-il comment je peux utiliser la même commande pour accéder aux sous-dossiers également?

UserYmY
la source

Réponses:

163

Dans Python 3.5 et plus récent, utilisez la nouvelle **/fonctionnalité récursive :

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

Lorsque recursiveest défini, **suivi d'un séparateur de chemin correspond à 0 ou plusieurs sous-répertoires.

Dans les versions antérieures de Python, glob.glob()impossible de répertorier les fichiers dans les sous-répertoires de manière récursive.

Dans ce cas, j'utiliserais os.walk()combiné avec à la fnmatch.filter()place:

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

Cela parcourra vos répertoires de manière récursive et retournera tous les chemins absolus vers les .txtfichiers correspondants . Dans ce cas précis, cela fnmatch.filter()peut être excessif, vous pouvez également utiliser 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')]
Martijn Pieters
la source
3
Je peux voir: glob.glob ('/ chemin vers le répertoire / * / *. Txt ") travailler pour moi. Ceci utilise essentiellement la règle du shell Unix.
Surya
7
@ User123: cela ne répertorie pas les répertoires de manière récursive . Vous répertoriez tous les fichiers texte d' un niveau de profondeur , mais pas dans d'autres sous-répertoires ni même directement dans path to directory.
Martijn Pieters
1
Ce n'est pas complètement lié, mais pourquoi le paramétrage recursive=Falseavec la **/ fonctionnalité ne fournit-il pas la liste des fichiers uniquement dans le dossier donné, mais plutôt dans ses enfants?
Dr_Zaszuś
@ Dr_Zaszuś: désolé? **/donne une liste de noms de répertoires dans le répertoire de travail courant, car le modèle se termine par /, et avec recursive=Falsevous avez fondamentalement un double *, correspondant exactement à la même chose */, juste moins efficace.
Martijn Pieters
@ Dr_Zaszuś: à utiliser */*si vous avez besoin de tous les fichiers de tous les sous-répertoires.
Martijn Pieters
22

Pour rechercher des fichiers dans des sous-répertoires immédiats:

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

Pour une version récursive qui traverse tous les sous-répertoires, vous pouvez utiliser **et passer recursive=True depuis Python 3.5 :

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

Les deux fonctions appellent des listes de retour. Vous pouvez utiliser glob.iglob()pour renvoyer les chemins un par un. Ou utilisezpathlib :

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

Les deux méthodes renvoient des itérateurs (vous pouvez obtenir les chemins un par un).

jfs
la source
Oui, j'ai compris cela; mais je ne m'attendais pas non plus glob()à prendre en charge les modèles dans les répertoires.
Martijn Pieters
Commentaire supprimé, je vois maintenant qu'il a donné une mauvaise impression; en outre, le patch inclut une mise à jour de la documentation pour le **cas de récursivité. Mais pour **fonctionner, vous devez régler le recursion=Truecommutateur, btw.
Martijn Pieters
20

Il y a beaucoup de confusion sur ce sujet. Voyons si je peux le clarifier (Python 3.7):

  1. glob.glob('*.txt') :correspond à tous les fichiers se terminant par '.txt' dans le répertoire courant
  2. glob.glob('*/*.txt') :idem 1
  3. glob.glob('**/*.txt') :correspond à tous les fichiers se terminant par '.txt' dans les sous-répertoires immédiats uniquement , mais pas dans le répertoire courant
  4. glob.glob('*.txt',recursive=True) :idem 1
  5. glob.glob('*/*.txt',recursive=True) :idem 3
  6. glob.glob('**/*.txt',recursive=True):correspond à tous les fichiers se terminant par '.txt' dans le répertoire courant et dans tous les sous-répertoires

Il est donc préférable de toujours spécifier recursive=True.

germe
la source
1
Cela devrait être la meilleure réponse!
Abhik Sarkar
17

Le package glob2 prend en charge les caractères génériques et est raisonnablement rapide

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

Sur mon ordinateur portable, il faut environ 2 secondes pour correspondre à plus de 60 000 chemins de fichiers .

mégawac
la source
9

Vous pouvez utiliser Formic avec Python 2.6

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

Divulgation - Je suis l'auteur de ce package.

Andrew Alcock
la source
4

Voici une version adaptée qui permet des glob.globfonctionnalités similaires sans utiliser 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

Donc, si vous avez la structure de répertoire suivante

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

Tu peux faire quelque chose comme ça

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

Le fnmatchmodèle correspond à peu près au nom de fichier entier lui-même, plutôt qu'au nom de fichier uniquement.

cevaris
la source
2

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

Ne fonctionne pas pour tous les cas, utilisez plutôt glob2

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

Si vous pouvez installer le package glob2 ...

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

Tous les noms de fichiers et dossiers:

all_ff = glob2.glob("C:\\top_directory\\**\\**")  
dreab
la source
2

Si vous exécutez Python 3.4+, vous pouvez utiliser le pathlibmodule. La Path.glob()méthode prend en charge le **modèle, qui signifie «ce répertoire et tous les sous-répertoires, de manière récursive». Il renvoie un générateur produisant des Pathobjets pour tous les fichiers correspondants.

from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")
Eugène Yarmash
la source
0

Comme l'a souligné Martijn, glob ne peut le faire que via l' **opérateur introduit dans Python 3.5. Puisque l'OP a explicitement demandé le module glob, ce qui suit renverra un itérateur d'évaluation paresseux qui se comporte de la même manière

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

Notez que vous ne pouvez effectuer une itération qu'une seule fois configfilesdans cette approche. Si vous avez besoin d'une véritable liste de fichiers de configuration pouvant être utilisés dans plusieurs opérations, vous devrez la créer explicitement en utilisant list(configfiles).

f0xdx
la source
0

La commande rglobeffectuera une récursion infinie dans le sous-niveau le plus profond de votre structure de répertoires. Si vous ne voulez qu'un seul niveau de profondeur, ne l'utilisez cependant pas.

Je me rends compte que l'OP parlait d'utiliser glob.glob. Je crois que cela répond à l'intention, cependant, qui est de rechercher tous les sous-dossiers de manière récursive.

La rglobfonction a récemment produit une augmentation de 100 fois de la vitesse pour un algorithme de traitement de données qui utilisait la structure des dossiers comme hypothèse fixe pour l'ordre de lecture des données. Cependant, rglobnous avons pu effectuer une seule analyse de tous les fichiers dans ou en dessous d'un répertoire parent spécifié, enregistrer leurs noms dans une liste (plus d'un million de fichiers), puis utiliser cette liste pour déterminer quels fichiers nous devions ouvrir à tout moment. point dans le futur en fonction des conventions de dénomination des fichiers uniquement par rapport au dossier dans lequel ils se trouvaient.

brethvoice
la source