Comment utiliser glob () pour rechercher des fichiers récursivement?


738

Voici ce que j'ai:

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

mais je veux rechercher les sous-dossiers de src. Quelque chose comme ça fonctionnerait:

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

Mais c'est évidemment limité et maladroit.

Réponses:


1355

Python 3.5+

Puisque vous êtes sur un nouveau python, vous devez utiliser à pathlib.Path.rglobpartir du pathlibmodule.

from pathlib import Path

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

Si vous ne voulez pas utiliser pathlib, utilisez simplement glob.glob, mais n'oubliez pas de passer le recursiveparamètre de mot - clé.

Pour les cas où les fichiers correspondants commençant par un point (.); comme les fichiers du répertoire courant ou les fichiers cachés sur un système basé sur Unix, utilisez la os.walksolution ci-dessous.

Versions Python plus anciennes

Pour les anciennes versions de Python, utilisez os.walkpour parcourir récursivement un répertoire et fnmatch.filterpour faire correspondre une expression 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))

3
Pour Python plus ancien que 2.2, il y en a un os.path.walk()qui est un peu plus délicat à utiliser queos.walk()
John La Rooy

20
@gnibbler Je sais que c'est un vieux commentaire, mais mon commentaire est juste de faire savoir aux gens qu'il os.path.walk()est obsolète et a été supprimé en Python 3.
Pedro Cunha

5
@DevC qui pourrait fonctionner dans le cas spécifique posé dans cette question, mais il est facile d'imaginer quelqu'un qui veut l'utiliser avec des requêtes telles que `` a * .c '', etc., donc je pense que cela vaut la peine de garder la réponse actuelle un peu lente.
Johan Dahlin

2
Pour ce que ça vaut, dans mon cas, trouver plus de 10 000 fichiers avec glob était beaucoup plus lent qu'avec os.walk, alors j'ai opté pour cette dernière solution pour cette raison.
Godsmith

2
Pour python 3.4, pathlib.Path('src').glob('**/*.c')devrait fonctionner.
CivFan

111

Similaire à d'autres solutions, mais en utilisant fnmatch.fnmatch au lieu de glob, car os.walk a déjà répertorié les noms de fichiers:

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

De plus, l'utilisation d'un générateur vous permet de traiter chaque fichier tel qu'il est trouvé, au lieu de rechercher tous les fichiers , puis de les traiter.


3
parce que les 1-liners sont amusants:reduce(lambda x, y: x+y, map(lambda (r,_,x):map(lambda f: r+'/'+f, filter(lambda f: fnmatch.fnmatch(f, pattern), x)), os.walk('src/webapp/test_scripts')))
njzk2

1
@ njzk2(os.path.join(root,filename) for root, dirs, files in os.walk(directory) for filename in files if fnmatch.fnmatch(filename, pattern))
Baldrickk

73

J'ai modifié le module glob pour prendre en charge ** la globalisation récursive, par exemple:

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

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

Utile lorsque vous souhaitez donner à vos utilisateurs la possibilité d'utiliser la syntaxe **, et donc os.walk () seul n'est pas suffisant.


2
Pouvons-nous faire cet arrêt après avoir trouvé le premier match? Peut-être permettre de l'utiliser comme générateur plutôt que de lui renvoyer une liste de tous les résultats possibles? Est-ce aussi un DFS ou un BFS? Je préfère de loin un BFS, je pense, pour que les fichiers proches de la racine soient trouvés en premier. +1 pour avoir créé ce module et l'avoir fourni sur GitHub / pip.
ArtOfWarfare

14
La syntaxe ** a été ajoutée au module glob officiel de Python 3.5.
ArtOfWarfare

@ArtOfWarfare Très bien, très bien. Ceci est toujours utile pour <3.5.
cs95

1
Pour activer la globalisation récursive en utilisant **avec le module officiel de glob, faites:glob(path, recursive=True)
winklerrr

68

À partir de Python 3.4, on peut utiliser la glob()méthode de l'une des Pathclasses du nouveau module pathlib , qui prend en charge les **caractères génériques. Par exemple:

from pathlib import Path

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

Mise à jour: à partir de Python 3.5, la même syntaxe est également prise en charge par glob.glob().


3
En effet, et ce sera en Python 3.5 . Il était censé l'être déjà dans Python 3.4, mais a été omis par erreur .
taleinat


Notez que vous pouvez également utiliser pathlib.PurePath.relative_to en combinaison pour obtenir des chemins relatifs. Voir ma réponse ici pour plus de contexte.
pjgranahan

40
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

fnmatchvous donne exactement les mêmes modèles que glob, donc c'est vraiment un excellent remplacement pour glob.globune sémantique très proche. Une version itérative (par exemple un générateur), IOW en remplacement glob.iglob, est une adaptation triviale (juste yieldles résultats intermédiaires au fur et à mesure, au lieu de extendgénérer une seule liste de résultats à retourner à la fin).


1
Que pensez-vous de l'utilisation recursive_glob(pattern, treeroot='.')comme je l'ai suggéré dans mon montage? De cette façon, il peut être appelé par exemple comme recursive_glob('*.txt')et correspondre intuitivement à la syntaxe de glob.
Chris Redford

@ChrisRedford, je le vois comme un problème assez mineur de toute façon. Dans l'état actuel des choses, il correspond à l'ordre d'argument "files then pattern" de fnmatch.filter, ce qui est à peu près aussi utile que la possibilité de faire correspondre un seul argument glob.glob.
Alex Martelli

25

Pour python> = 3.5 , vous pouvez utiliser **, recursive=True:

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

Démo


Si récursif est True, le modèle ** correspondra à tous les fichiers et à zéro ou plus directoriesetsubdirectories . Si le motif est suivi d'unos.sep , seuls les répertoires et les subdirectoriescorrespondances.


2
Cela fonctionne mieux que pathlib.Path ('./ path /'). Glob (' * / ') car il en est de même dans le dossier de taille 0
Charles Walker

20

Vous voudrez utiliser os.walkpour collecter des noms de fichiers qui correspondent à vos critères. Par exemple:

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

15

Voici une solution avec des listes de listes imbriquées os.walket une correspondance de suffixe simple au lieu de glob:

import os
cfiles = [os.path.join(root, filename)
          for root, dirnames, filenames in os.walk('src')
          for filename in filenames if filename.endswith('.c')]

Il peut être compressé en une seule ligne:

import os;cfiles=[os.path.join(r,f) for r,d,fs in os.walk('src') for f in fs if f.endswith('.c')]

ou généralisé en fonction:

import os

def recursive_glob(rootdir='.', suffix=''):
    return [os.path.join(looproot, filename)
            for looproot, _, filenames in os.walk(rootdir)
            for filename in filenames if filename.endswith(suffix)]

cfiles = recursive_glob('src', '.c')

Si vous avez besoin de globmodèles de style complet , vous pouvez suivre l'exemple d'Alex et de Bruno et utiliser fnmatch:

import fnmatch
import os

def recursive_glob(rootdir='.', pattern='*'):
    return [os.path.join(looproot, filename)
            for looproot, _, filenames in os.walk(rootdir)
            for filename in filenames
            if fnmatch.fnmatch(filename, pattern)]

cfiles = recursive_glob('src', '*.c')

7

Récemment, j'ai dû récupérer mes photos avec l'extension .jpg. J'ai couru photorec et récupéré 4579 répertoires dans 2,2 millions de fichiers, avec une grande variété d'extensions. Avec le script ci-dessous, j'ai pu sélectionner 50133 fichiers havin .jpg extension en quelques minutes:

#!/usr/binenv python2.7

import glob
import shutil
import os

src_dir = "/home/mustafa/Masaüstü/yedek"
dst_dir = "/home/mustafa/Genel/media"
for mediafile in glob.iglob(os.path.join(src_dir, "*", "*.jpg")): #"*" is for subdirectory
    shutil.copy(mediafile, dst_dir)

7

Considérez pathlib.rglob().

C'est comme appeler Path.glob()avec "**/"ajouté devant le modèle relatif donné:

import pathlib


for p in pathlib.Path("src").rglob("*.c"):
    print(p)

Voir aussi le post connexe de @ taleinat ici et un post similaire ailleurs.


5

Johan et Bruno fournissent d'excellentes solutions sur l'exigence minimale comme indiqué. Je viens de publier Formic qui implémente Ant FileSet et Globs qui peuvent gérer cela et des scénarios plus compliqués. Une implémentation de votre exigence est:

import formic
fileset = formic.FileSet(include="/src/**/*.c")
for file_name in fileset.qualified_files():
    print file_name

1
Le formel semble abandonné?! Et il ne prend pas en charge Python 3 ( bitbucket.org/aviser/formic/issue/12/support-python-3 )
blueyed le

5

sur la base d'autres réponses, voici mon implémentation de travail actuelle, qui récupère les fichiers xml imbriqués dans un répertoire racine:

files = []
for root, dirnames, filenames in os.walk(myDir):
    files.extend(glob.glob(root + "/*.xml"))

Je m'amuse vraiment avec python :)


3

Une autre façon de le faire en utilisant uniquement le module glob. Il suffit d'amorcer la méthode rglob avec un répertoire de base de départ et un modèle pour correspondre et il renverra une liste de noms de fichiers correspondants.

import glob
import os

def _getDirs(base):
    return [x for x in glob.iglob(os.path.join( base, '*')) if os.path.isdir(x) ]

def rglob(base, pattern):
    list = []
    list.extend(glob.glob(os.path.join(base,pattern)))
    dirs = _getDirs(base)
    if len(dirs):
        for d in dirs:
            list.extend(rglob(os.path.join(base,d), pattern))
    return list

3

Pour python 3.5 et versions ultérieures

import glob

#file_names_array = glob.glob('path/*.c', recursive=True)
#above works for files directly at path/ as guided by NeStack

#updated version
file_names_array = glob.glob('path/**/*.c', recursive=True)

plus vous pourriez avoir besoin

for full_path_in_src in  file_names_array:
    print (full_path_in_src ) # be like 'abc/xyz.c'
    #Full system path of this would be like => 'path till src/abc/xyz.c'

3
Votre première ligne de code ne fonctionne pas pour la recherche dans les sous-répertoires. Mais si vous le développez simplement, /**cela fonctionne pour moi, comme ça:file_names_array = glob.glob('src/**/*.c', recursive=True)
NeStack

2

Ou avec une compréhension de la liste:

 >>> base = r"c:\User\xtofl"
 >>> binfiles = [ os.path.join(base,f) 
            for base, _, files in os.walk(root) 
            for f in files if f.endswith(".jpg") ] 

2

Je viens de faire cela .. il imprimera les fichiers et le répertoire de manière hiérarchique

Mais je n'ai pas utilisé fnmatch ou walk

#!/usr/bin/python

import os,glob,sys

def dirlist(path, c = 1):

        for i in glob.glob(os.path.join(path, "*")):
                if os.path.isfile(i):
                        filepath, filename = os.path.split(i)
                        print '----' *c + filename

                elif os.path.isdir(i):
                        dirname = os.path.basename(i)
                        print '----' *c + dirname
                        c+=1
                        dirlist(i,c)
                        c-=1


path = os.path.normpath(sys.argv[1])
print(os.path.basename(path))
dirlist(path)

2

Celui-là utilise fnmatch ou expression régulière:

import fnmatch, os

def filepaths(directory, pattern):
    for root, dirs, files in os.walk(directory):
        for basename in files:
            try:
                matched = pattern.match(basename)
            except AttributeError:
                matched = fnmatch.fnmatch(basename, pattern)
            if matched:
                yield os.path.join(root, basename)

# usage
if __name__ == '__main__':
    from pprint import pprint as pp
    import re
    path = r'/Users/hipertracker/app/myapp'
    pp([x for x in filepaths(path, re.compile(r'.*\.py$'))])
    pp([x for x in filepaths(path, '*.py')])

2

En plus des réponses suggérées, vous pouvez le faire avec une génération de paresseux et une magie de compréhension de liste:

import os, glob, itertools

results = itertools.chain.from_iterable(glob.iglob(os.path.join(root,'*.c'))
                                               for root, dirs, files in os.walk('src'))

for f in results: print(f)

En plus de tenir sur une seule ligne et d'éviter les listes inutiles en mémoire, cela a aussi le bel effet secondaire, que vous pouvez l'utiliser d'une manière similaire à l'opérateur **, par exemple, vous pouvez utiliser os.path.join(root, 'some/path/*.c')pour obtenir tous les fichiers .c en tout sous-répertoires de src qui ont cette structure.


2

Il s'agit d'un code de travail sur Python 2.7. Dans le cadre de mon travail de devops, je devais écrire un script qui déplacerait les fichiers de configuration marqués avec live-appName.properties vers appName.properties. Il pourrait y avoir d'autres fichiers d'extension comme live-appName.xml.

Voici un code de travail pour cela, qui trouve les fichiers dans les répertoires donnés (niveau imbriqué) puis le renomme (déplace) au nom de fichier requis

def flipProperties(searchDir):
   print "Flipping properties to point to live DB"
   for root, dirnames, filenames in os.walk(searchDir):
      for filename in fnmatch.filter(filenames, 'live-*.*'):
        targetFileName = os.path.join(root, filename.split("live-")[1])
        print "File "+ os.path.join(root, filename) + "will be moved to " + targetFileName
        shutil.move(os.path.join(root, filename), targetFileName)

Cette fonction est appelée à partir d'un script principal

flipProperties(searchDir)

J'espère que cela aidera quelqu'un aux prises avec des problèmes similaires.


1

Version simplifiée de la réponse de Johan Dahlin, sans fnmatch .

import os

matches = []
for root, dirnames, filenames in os.walk('src'):
  matches += [os.path.join(root, f) for f in filenames if f[-2:] == '.c']

1

Voici ma solution en utilisant la compréhension de liste pour rechercher plusieurs extensions de fichiers récursivement dans un répertoire et tous les sous-répertoires:

import os, glob

def _globrec(path, *exts):
""" Glob recursively a directory and all subdirectories for multiple file extensions 
    Note: Glob is case-insensitive, i. e. for '\*.jpg' you will get files ending
    with .jpg and .JPG

    Parameters
    ----------
    path : str
        A directory name
    exts : tuple
        File extensions to glob for

    Returns
    -------
    files : list
        list of files matching extensions in exts in path and subfolders

    """
    dirs = [a[0] for a in os.walk(path)]
    f_filter = [d+e for d in dirs for e in exts]    
    return [f for files in [glob.iglob(files) for files in f_filter] for f in files]

my_pictures = _globrec(r'C:\Temp', '\*.jpg','\*.bmp','\*.png','\*.gif')
for f in my_pictures:
    print f

0
import sys, os, glob

dir_list = ["c:\\books\\heap"]

while len(dir_list) > 0:
    cur_dir = dir_list[0]
    del dir_list[0]
    list_of_files = glob.glob(cur_dir+'\\*')
    for book in list_of_files:
        if os.path.isfile(book):
            print(book)
        else:
            dir_list.append(book)

0

J'ai modifié la première réponse dans cette publication .. et récemment créé ce script qui parcourra tous les fichiers dans un répertoire donné (searchdir) et les sous-répertoires en dessous ... et imprime le nom de fichier, rootdir, la date de modification / création et Taille.

J'espère que cela aide quelqu'un ... et qu'il pourra parcourir le répertoire et obtenir fileinfo.

import time
import fnmatch
import os

def fileinfo(file):
    filename = os.path.basename(file)
    rootdir = os.path.dirname(file)
    lastmod = time.ctime(os.path.getmtime(file))
    creation = time.ctime(os.path.getctime(file))
    filesize = os.path.getsize(file)

    print "%s**\t%s\t%s\t%s\t%s" % (rootdir, filename, lastmod, creation, filesize)

searchdir = r'D:\Your\Directory\Root'
matches = []

for root, dirnames, filenames in os.walk(searchdir):
    ##  for filename in fnmatch.filter(filenames, '*.c'):
    for filename in filenames:
        ##      matches.append(os.path.join(root, filename))
        ##print matches
        fileinfo(os.path.join(root, filename))

0

Voici une solution qui fera correspondre le modèle au chemin complet et pas seulement au nom de fichier de base.

Il utilise fnmatch.translatepour convertir un modèle de style glob en une expression régulière, qui est ensuite comparée au chemin complet de chaque fichier trouvé lors de la marche dans le répertoire.

re.IGNORECASEest facultatif, mais souhaitable sous Windows car le système de fichiers lui-même n'est pas sensible à la casse. (Je n'ai pas pris la peine de compiler l'expression régulière, car les documents indiquent qu'elle doit être mise en cache en interne.)

import fnmatch
import os
import re

def findfiles(dir, pattern):
    patternregex = fnmatch.translate(pattern)
    for root, dirs, files in os.walk(dir):
        for basename in files:
            filename = os.path.join(root, basename)
            if re.search(patternregex, filename, re.IGNORECASE):
                yield filename

0

J'avais besoin d'une solution pour python 2.x qui fonctionne rapidement sur les grands répertoires.
Je me retrouve avec ceci:

import subprocess
foundfiles= subprocess.check_output("ls src/*.c src/**/*.c", shell=True)
for foundfile in foundfiles.splitlines():
    print foundfile

Notez que vous pourriez avoir besoin d'une gestion des exceptions au cas où lsil ne trouverait aucun fichier correspondant.


Je viens de réaliser que cela ls src/**/*.cne fonctionne que si l'option globstar est activée ( shopt -s globstar) - voir cette réponse pour plus de détails.
Roman
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.