【问题标题】:How to use glob() to find files recursively?如何使用 glob() 递归查找文件?
【发布时间】:2011-01-12 07:01:25
【问题描述】:

这就是我所拥有的:

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

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

【问题讨论】:

  • glob('src/**/*.c') 在这种情况下不起作用?

标签: python path filesystems glob fnmatch


【解决方案1】:

pathlib.Path.rglob

使用pathlib 模块中的pathlib.Path.rglob,该模块是在 Python 3.5 中引入的。

from pathlib import Path

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

如果你不想使用pathlib,可以使用glob.glob('**/*.c'),但不要忘记传入recursive关键字参数,它会在大目录上占用过多的时间。

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

os.walk

对于较旧的 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))

【讨论】:

  • 对于早于 2.2 的 Python,os.path.walk() 使用起来比 os.walk() 稍微复杂一点
  • @gnibbler 我知道这是一个旧评论,但我的评论只是为了让人们知道 os.path.walk() 已被弃用并已在 Python 3 中删除。
  • @DevC 可能适用于这个问题中提出的特定情况,但很容易想象有人想要使用它来处理诸如 'a*.c' 等查询,所以我认为它是值得保留当前有点慢的答案。
  • 对于我来说,使用 glob 查找 10,000 多个文件比使用 os.walk 慢得多,因此我选择了后一种解决方案。
  • 对于 python 3.4,pathlib.Path('src').glob('**/*.c') 应该可以工作。
【解决方案2】:

与其他解决方案类似,但使用 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

此外,使用生成器可以让您在找到每个文件时对其进行处理,而不是找到所有文件然后处理它们。

【讨论】:

    【解决方案3】:

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

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

    如果递归是True,模式** 将匹配任何文件和零 或更多 directoriessubdirectories。如果模式后面跟着 os.sep,只有目录和 subdirectories 匹配。


    Python 3.6 Demo

    【讨论】:

    • 这比 pathlib.Path('./path/').glob('*/') 更好,因为它在大小为 0 的文件夹中也是如此跨度>
    • 在 Python 3.9.1 中,recursive 默认设置为 False。
    • recursive 在 Python 3.8.* 中也默认设置为 False
    【解决方案4】:

    我已修改 glob 模块以支持 ** 进行递归通配,例如:

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

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

    当您希望为用户提供使用 ** 语法的能力时很有用,因此单独使用 os.walk() 还不够好。

    【讨论】:

    • 在找到第一个匹配项后我们可以停止吗?也许可以将它用作生成器,而不是让它返回每个可能结果的列表?另外,这是 DFS 还是 BFS?我认为我更喜欢 BFS,以便首先找到根附近的文件。 +1 用于制作此模块并在 GitHub/pip 上提供。
    • Python 3.5 的官方 glob 模块中添加了 ** 语法。
    • @ArtOfWarfare 好的,好的。这对于
    • 要通过官方 glob 模块使用 ** 激活递归 glob,请执行以下操作:glob(path, recursive=True)
    【解决方案5】:

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

    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() 也支持相同的语法。

    【讨论】:

    【解决方案6】:
    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 在最后返回单个结果列表)。

    【讨论】:

    • 您如何看待我在编辑中建议的使用recursive_glob(pattern, treeroot='.')?这样,它可以被称为recursive_glob('*.txt'),并且直观地匹配glob的语法。
    • @ChrisRedford,无论哪种方式,我都认为这是一个很小的问题。就目前而言,它匹配fnmatch.filter 的“文件然后模式”参数顺序,这与匹配单个参数glob.glob 的可能性大致相同。
    【解决方案7】:

    您需要使用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))
    

    【讨论】:

      【解决方案8】:

      这是一个带有嵌套列表推导、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')
      

      【讨论】:

        【解决方案9】:

        考虑pathlib.rglob()

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

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

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

        【讨论】:

          【解决方案10】:
          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结尾的文件

          【讨论】:

            【解决方案11】:

            最近我不得不用扩展名 .jpg 恢复我的图片。我运行 photorec 并恢复了 4579 个目录,其中包含 220 万个文件,扩展名种类繁多。使用下面的脚本,我能够在几分钟内选择 50133 个具有 .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)
            

            【讨论】:

              【解决方案12】:

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

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

              我真的很喜欢 python :)

              【讨论】:

                【解决方案13】:

                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
                

                【讨论】:

                【解决方案14】:

                适用于 python 3.5 及更高版本

                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)
                

                你可能还需要

                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'
                

                【讨论】:

                • 您的第一行代码不适用于查看子目录。但是,如果您只是将其扩展为 /** 它对我有用,就像这样:file_names_array = glob.glob('src/**/*.c', recursive=True)
                【解决方案15】:

                如果有人对此感兴趣,我已经介绍了前三种建议的方法。 我在 globbed 文件夹中有大约 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.6sec
                rglob ~14.5 秒
                iglob:~16.9 秒

                平台:Ubuntu 16.04, x86_64 (core i7),

                【讨论】:

                  【解决方案16】:

                  另一种仅使用 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
                  

                  【讨论】:

                    【解决方案17】:

                    或者使用列表理解:

                     >>> 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】:

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

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

                      【讨论】:

                        【解决方案19】:

                        那个使用 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')])
                        

                        【讨论】:

                          【解决方案20】:

                          除了建议的答案之外,您还可以使用一些惰性生成和列表理解魔法来做到这一点:

                          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 文件。

                          【讨论】:

                            【解决方案21】:

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

                            希望这对遇到类似问题的人有所帮助。

                            【讨论】:

                              【解决方案22】:

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

                              【讨论】:

                                【解决方案23】:

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

                                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
                                

                                【讨论】:

                                  【解决方案24】:

                                  如果文件位于远程文件系统存档内,您可以使用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

                                  【讨论】:

                                    【解决方案25】:
                                    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)
                                    

                                    【讨论】:

                                      【解决方案26】:

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

                                      希望这对某人有所帮助......他们可以遍历目录并获取文件信息。

                                      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】:

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

                                        它使用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
                                        

                                        【讨论】:

                                          【解决方案28】:

                                          我需要一个 python 2.x 解决方案,它可以快速 在大型目录上运行。
                                          我同意这个:

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

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

                                          【讨论】:

                                          • 我刚刚意识到 ls src/**/*.c 仅在启用 globstar 选项时才有效 (shopt -s globstar) - 详情请参阅 this answer
                                          • 如果你想快点,子进程永远不是一个好的解决方案,ls in scripts is definitely something to avoid.
                                          • 好吧,我不知道这个。它对我有用 - 不到一秒(而不是超过 30 秒......)
                                          猜你喜欢
                                          • 1970-01-01
                                          • 2011-08-31
                                          • 1970-01-01
                                          • 1970-01-01
                                          相关资源
                                          最近更新 更多