如何使用glob()递归查找文件?

时间:2010-02-02 18:19:50

标签: python path filesystems glob fnmatch

这就是我所拥有的:

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

但我想搜索src的子文件夹。像这样的东西会起作用:

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

但这显然是有限和笨重的。

28 个答案:

答案 0 :(得分:1138)

Python 3.5 +

由于你正在使用新的python,你应该使用pathlib.Path.glob模块中的pathlib

from pathlib import Path

for filename in Path('src').glob('**/*.c'):
    print(filename)

如果您不想使用路径库,请使用glob.glob,但不要忘记传递recursive关键字参数。

对于匹配以点(。)开头的文件的情况;如当前目录中的文件或基于Unix的系统上的隐藏文件,请使用下面的os.walk解决方案。

较旧的Python版本

对于较旧的Python版本,使用os.walk递归遍历目录,使用fnmatch.filter匹配简单表达式:

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

答案 1 :(得分:103)

与其他解决方案类似,但使用fnmatch.fnmatch而不是glob,因为os.walk已经列出了文件名:

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

此外,使用生成器可以处理找到的每个文件,而不是找到所有文件,然后处理它们。

答案 2 :(得分:60)

我修改了glob模块以支持**用于递归globbing,例如:

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

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

当你想为你的用户提供使用**语法的能力时很有用,因此单独使用os.walk()是不够的。

答案 3 :(得分:56)

从Python 3.4开始,可以使用新glob()模块中Path个类之一的pathlib方法,该模块支持**通配符。例如:

from pathlib import Path

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

<强>更新 从Python 3.5开始,glob.glob()也支持相同的语法。

答案 4 :(得分:39)

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为您提供与glob完全相同的模式,因此这对于glob.glob非常接近,具有非常接近的语义。迭代版本(例如生成器),IOW替换glob.iglob,是一个简单的适应(只有yield中间结果,而不是extend单个结果列表到最后回来)。

答案 5 :(得分:20)

您需要使用os.walk来收集符合条件的文件名。例如:

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

答案 6 :(得分:15)

以下是嵌套列表推导的解决方案,os.walk和简单的后缀匹配,而不是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')]

它可以压缩成一行:

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

或概括为函数:

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

如果您确实需要完整的glob样式模式,则可以关注Alex和 布鲁诺的例子并使用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 :(得分:7)

对于python> = 3.5,您可以使用**recursive=True

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

Demo


  

如果递归为true,则模式** 将匹配任何文件,并且为零   或更多directoriessubdirectories。如果模式后面跟随   os.sep,只有目录和subdirectories匹配。

答案 8 :(得分:5)

基于其他答案,这是我当前的工作实现,它在根目录中检索嵌套的xml文件:

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

我真的很开心python:)

答案 9 :(得分:5)

最近我不得不使用扩展名.jpg恢复我的照片。我运行了photorec并恢复了4579个目录,其中有220万个文件,有各种各样的扩展名。通过下面的脚本,我能够在几分钟内选择50133个文件havin .jpg扩展名:

#!/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)

答案 10 :(得分:5)

Johan和Bruno就所述的最低要求提供了出色的解决方案。我刚刚发布了Formic,它实现了Ant FileSet and Globs,它可以处理这个和更复杂的场景。您的要求的实施是:

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

答案 11 :(得分:3)

使用glob模块的另一种方法。只需使用起始基目录和匹配的模式对rglob方法进行种子处理,它将返回匹配文件名列表。

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

答案 12 :(得分:3)

import os, glob

for each in glob.glob('path/**/*.c', recursive=True):
    print(f'Name with path: {each} \nName without path: {os.path.basename(each)}')
  • glob.glob('*.c') :匹配当前目录中所有以.c结尾的文件
  • glob.glob('*/*.c') :与1相同
  • glob.glob('**/*.c') :仅匹配直接子目录中所有以.c结尾的文件,而不匹配当前目录中的所有文件
  • glob.glob('*.c',recursive=True) :与1相同
  • glob.glob('*/*.c',recursive=True) :与3相同
  • glob.glob('**/*.c',recursive=True) :匹配当前目录和所有子目录中所有以.c结尾的文件

答案 13 :(得分:2)

刚刚做了..它将以分层方式打印文件和目录

但我没有使用fnmatch或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)

答案 14 :(得分:2)

那个使用fnmatch或正则表达式:

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

答案 15 :(得分:2)

除了建议的答案之外,您还可以使用一些懒惰的生成和列表理解魔术:

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)

除了适合一行并避免在内存中使用不必要的列表外,这也有很好的副作用,你可以用类似**运算符的方式使用它,例如,你可以按顺序使用os.path.join(root, 'some/path/*.c')获取具有此结构的src的所有子目录中的所有.c文件。

答案 16 :(得分:1)

Johan Dahlin答案的简化版,没有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']

答案 17 :(得分:1)

或者列表理解:

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

答案 18 :(得分:1)

以下是我使用列表推导在目录和所有子目录中搜索多个文件扩展名递归的解决方案:

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

答案 19 :(得分:1)

万一这可能使任何人感兴趣,我介绍了提出的前三种方法。 我在全局文件夹中总共有大约500K个文件,并且有2K个文件符合所需的模式。

这是(非常基本的)代码

import glob
import json
import fnmatch
import os
from pathlib import Path
from time import time


def find_files_iglob():
    return glob.iglob("./data/**/data.json", recursive=True)


def find_files_oswalk():
    for root, dirnames, filenames in os.walk('data'):
        for filename in fnmatch.filter(filenames, 'data.json'):
            yield os.path.join(root, filename)

def find_files_rglob():
    return Path('data').rglob('data.json')

t0 = time()
for f in find_files_oswalk(): pass    
t1 = time()
for f in find_files_rglob(): pass
t2 = time()
for f in find_files_iglob(): pass 
t3 = time()
print(t1-t0, t2-t1, t3-t2)

我得到的结果是:
os_walk:〜3.6秒
rglob〜14.5sec
iglob:〜16.9秒

平台:Ubuntu 16.04,x86_64(核心i7),

答案 20 :(得分:1)

这是Python 2.7上的有效代码。作为我的devops工作的一部分,我需要编写一个脚本,该脚本会将标有live-appName.properties的配置文件移动到appName.properties。可能还有其他扩展文件,例如live-appName.xml。

下面是为此工作的代码,该代码在给定目录(嵌套级别)中查找文件,然后将其重命名(移动)为所需的文件名

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)

此功能从主脚本中调用

flipProperties(searchDir)

希望这可以帮助遇到类似问题的人。

答案 21 :(得分:1)

考虑pathlib.rglob()

  

这就像调用Path.glob()并在给定相对模式前面添加"**/"一样:

import pathlib


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

另请参阅@taleinat的相关post和其他地方的post

答案 22 :(得分:0)

如果文件位于远程文件系统存档内部,则可以使用fsspec AbstractFileSystem class的实现。例如,要列出zipfile中的所有文件:

from fsspec.implementations.zip import ZipFileSystem
fs = ZipFileSystem("/tmp/test.zip")
fs.glob("/**")  # equivalent: fs.find("/")

或列出公共可用的S3存储桶中的所有文件:

from s3fs import S3FileSystem
fs_s3 = S3FileSystem(anon=True)
fs_s3.glob("noaa-goes16/ABI-L1b-RadF/2020/045/**")  # or use fs_s3.find

您还可以将其用于本地文件系统,如果您的实现与文件系统无关,那么这可能会很有趣:

from fsspec.implementations.local import LocalFileSystem
fs = LocalFileSystem()
fs.glob("/tmp/test/**")

其他实现包括Google Cloud,Github,SFTP / SSH,Dropbox和Azure。有关详细信息,请参见fsspec API documentation

答案 23 :(得分:0)

对于python 3.5及更高版本

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

您可能还需要

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'

答案 24 :(得分:0)

我需要 python 2.x 的解决方案,在大型目录上快速
我很喜欢这个:

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

请注意,如果ls找不到任何匹配的文件,您可能需要进行一些异常处理。

答案 25 :(得分:0)

这是一个解决方案,它将模式与完整路径匹配,而不仅仅是基本文件名。

它使用fnmatch.translate将glob样式模式转换为正则表达式,然后将其与遍历目录时找到的每个文件的完整路径进行匹配。

re.IGNORECASE是可选的,但在Windows上是可取的,因为文件系统本身不区分大小写。 (我没有费心编译正则表达式,因为文档表明它应该在内部缓存。)

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

答案 26 :(得分:0)

我在这篇帖子中修改了最常见的答案..最近创建了这个脚本,它将遍历给定目录(searchdir)中的所有文件及其下的子目录...并打印文件名,rootdir,修改/创建日期和大小。

希望这可以帮助某人......他们可以走目录并获取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))

答案 27 :(得分: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)