web-dev-qa-db-fra.com

Utilisez un Glob () pour rechercher des fichiers récursivement en Python?

Voici ce que j'ai

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

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

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 ceci est évidemment limité et maladroit.

564
Ben Gartner

Python 3.5+

À partir de Python version 3.5, le module glob prend en charge la directive "**" (qui est analysée uniquement si vous passez l'indicateur recursive):

import glob

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

Si vous avez besoin d’une liste, utilisez simplement glob.glob au lieu de glob.iglob .

Pour les cas où les fichiers correspondants commencent par un point (.); comme des fichiers du répertoire en cours ou des fichiers cachés sur un système Unix, utilisez la solution os.walk ci-dessous.

Python 2.2 à 3.4

Pour les anciennes versions de Python, à partir de Python 2.2, utilisez os.walk pour parcourir de manière récursive un répertoire et fnmatch.filter pour 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))

Python 2.1 et versions antérieures

Pour les versions encore plus anciennes de Python, utilisez glob.glob contre chaque nom de fichier au lieu de fnmatch.filter .

1083
Johan Dahlin

Semblable à d'autres solutions, mais en utilisant fnmatch.fnmatch au lieu de glob, puisque 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

En outre, 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 et ensuite de les traiter.

102
Bruno Oliveira

J'ai modifié le module glob afin de prendre en charge ** les modifications globales récursives, par exemple:

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

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

Utile lorsque vous souhaitez que vos utilisateurs puissent utiliser la syntaxe **. Ainsi, os.walk () seul ne suffit pas.

56
miracle2k

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

from pathlib import Path

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

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

49
taleinat
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 vous donne exactement les mêmes modèles que glob , donc c'est vraiment un excellent remplacement pour glob.glob avec une sémantique très proche. Une version itérative (par exemple un générateur), qui remplace IOW par glob.iglob, est une adaptation triviale (juste yield les résultats intermédiaires au fur et à mesure, au lieu de extending une liste de résultats unique à renvoyer à la fin).

39
Alex Martelli

Vous voudrez utiliser os.walk pour collecter les noms de fichiers correspondant à 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))
20
Geoff Reedy

Voici une solution avec des compréhensions de liste imbriquées, os.walk et un suffixe simple correspondant à la place 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 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 modèles de style glob complets, vous pouvez suivre l'exemple d'Alex et 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')
14
akaihola

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 avec l'extension .jpg 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)
5
Mustafa Çetin

Johan et Bruno apportent d’excellentes solutions aux exigences minimales énoncées. 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 besoin est:

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

sur la base d’autres réponses, c’est 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 :)

5
daveoncode

Une autre façon de le faire en utilisant simplement le module glob. Ensemencez simplement la méthode rglob avec un répertoire de base de départ et un modèle de correspondance, ce qui 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
chris-piekarski

En plus des réponses suggérées, vous pouvez le faire avec une magie paresseuse de la génération et de la compréhension de la 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 d'insérer une ligne et d'éviter des listes inutiles en mémoire, cela a également pour effet secondaire que vous pouvez l'utiliser de la même manière que l'opérateur **, par exemple, vous pouvez utiliser os.path.join(root, 'some/path/*.c') pour obtenir tous les fichiers .c tous les sous-répertoires de src qui ont cette structure.

2
fxx

Vient de faire cela .. il va imprimer les fichiers et le répertoire de manière hiérarchique

Mais je n'ai pas utilisé fnmatch ou marcher

#!/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
Shaurya Gupta

Considérons pathlib.rglob() .

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

import pathlib


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

Voir aussi @ post ici et un post _ _ précédent (taleinat) ailleurs.

1
pylang

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 __== '__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')])
1
hipertracker

Voici ma solution en utilisant la compréhension de liste pour rechercher plusieurs extensions de fichiers de manière récursive 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
1
sackpower

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
flowfree

Pour python> = .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 vrai, le modèle ** correspond à tous les fichiers et à zéro ou plus directories et subdirectories. Si le motif est suivi d'un os.sep, seuls les répertoires et subdirectories correspondent.

1
Pedro Lobito

Ou avec une compréhension de 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") ] 
1
xtofl

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

Il utilise fnmatch.translate pour convertir un motif de style global en une expression régulière, qui est ensuite comparée au chemin complet de chaque fichier trouvé lors de la consultation du répertoire.

re.IGNORECASE est 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 rationnelle car la documentation indique qu'elle devrait ê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
yoyo

Pour python 3.5 et versions ultérieures

file_names_array = glob.glob('src/*.c', recursive=True)

Edit: Comme @NeStack guidé si ci-dessus ne fonctionne pas pour vous, s'il vous plaît essayez

file_names_array = glob.glob('src/**.c', recursive=True)

de 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'
0
Sami
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
serega386

J'ai modifié la première réponse dans cette publication .. et récemment créé ce script qui parcourt tous les fichiers d'un répertoire donné (searchdir) et des sous-répertoires en dessous ... Taille.

J'espère que cela aide quelqu'un ... et ils peuvent 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
ihightower

J'avais besoin d'une solution pour python 2.x qui fonctionne rapide sur les grands répertoires.
Je termine 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 aurez peut-être besoin d'une gestion des exceptions au cas où ls ne trouve aucun fichier correspondant.

0
Roman