使用Glob()在Python中递归查找文件?

Ben*_*ner 676 python filesystems glob path fnmatch

这就是我所拥有的:

glob(os.path.join('src','*.c'))
Run Code Online (Sandbox Code Playgroud)

但我想搜索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'))
Run Code Online (Sandbox Code Playgroud)

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

Joh*_*lin 1251

Python 3.5+

从Python 3.5版开始,该pathlib.Path.rglob模块支持该pathlib指令(只有在传递glob.globflag时才会解析):

from pathlib import Path

for filename in Path('src').rglob('*.c'):
    print(filename)
Run Code Online (Sandbox Code Playgroud)

如果您需要一个列表,只需使用recursive而不是os.walk.

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

Python 2.2到3.4

对于较旧的Python版本,从Python 2.2开始,使用fnmatch.filter递归遍历目录并pathlib.Path.rglob匹配简单表达式:

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))
Run Code Online (Sandbox Code Playgroud)

Python 2.1及更早版本

对于更老的Python版本,使用pathlib针对每个文件名而不是glob.glob.

  • @gnibbler我知道这是一个旧的评论,但我的评论只是让人们知道`os.path.walk()`已被弃用,并已在Python 3中删除. (17认同)
  • @DevC可能会在这个问题的特定情况下工作,但是很容易想象有人想要用'a*.c'之类的查询来使用它,所以我认为值得保持当前有点慢的答案. (4认同)
  • 对于2.2以上的Python,有一个`os.path.walk()`,它比`os.walk()更加繁琐. (3认同)
  • 对于 python 3.4,`pathlib.Path('src').glob('**/*.c')` 应该可以工作。 (3认同)
  • 就其价值而言,以我为例,使用glob查找10,000+个文件比使用os.walk要慢得多,因此出于这个原因,我选择了后一种解决方案。 (2认同)
  • @JohanDahlin我注意到你已经更新了你原来的帖子(建议`os.walk`和`glob.glob`),答案已经发布:`fnmatch.filter`(@Alex Martelli),`glob.glob`(@chris- iekarski)、“pathlib.Path.glob”(@taleinat)和现在的“pathlib.Path.rglob”。我认为这是无意的。我可以建议您尽可能引用更新之前的帖子吗?否则,激励您更新的其他人的出色工作可能会被忽视。感谢您不断更新您的帖子。 (2认同)

Bru*_*ira 107

与其他解决方案类似,但使用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
Run Code Online (Sandbox Code Playgroud)

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

  • 因为1-liners很有趣:`reduce(lambda x,y:x + y,map(lambda(r,_,x):map(lambda f:r +'/'+ f,filter(lambda f:fnmatch.fnmatch) (F,图案)中,x)),os.walk( '的src/web应用/ test_scripts')))` (3认同)

mir*_*e2k 65

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

>>> import glob2
>>> all_header_files = glob2.glob('src/**/*.c')
Run Code Online (Sandbox Code Playgroud)

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

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

  • **语法被添加到Python 3.5中的官方glob模块中. (11认同)
  • 要使用官方 glob 模块使用“**”激活递归通配符,请执行:“glob(path, recursive=True)” (6认同)
  • 我们可以在找到第一个匹配项后停止吗?也许可以将它用作生成器而不是让它返回每个可能结果的列表?另外,这是DFS还是BFS?我认为我更喜欢 BFS,以便首先找到靠近根目录的文件。+1 用于制作此模块并在 GitHub/pip 上提供。 (2认同)

tal*_*nat 62

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

from pathlib import Path

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

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

  • 的确,[它将在Python 3.5中](https://hg.python.org/cpython/rev/ff4b9d654691/).它应该在Python 3.4中已经如此,但是[错误地省略](http://bugs.python.org/issue13968#msg212878). (3认同)

Ale*_*lli 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
Run Code Online (Sandbox Code Playgroud)

fnmatch给你完全相同的模式glob,所以这是glob.glob非常接近的语义的一个很好的替代品.一个迭代版本(例如一个生成器),IOW替换glob.iglob,是一个简单的适应(只是yield你去的中间结果,而不是extend在结束时返回单个结果列表).


Geo*_*edy 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))
Run Code Online (Sandbox Code Playgroud)


aka*_*ola 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')]
Run Code Online (Sandbox Code Playgroud)

它可以压缩成一行:

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

或者概括为一个函数:

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')
Run Code Online (Sandbox Code Playgroud)

如果你确实需要完整的glob样式模式,你可以按照亚历克斯和布鲁诺的例子使用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')
Run Code Online (Sandbox Code Playgroud)


pyl*_*ang 9

考虑pathlib.rglob()

这就好比调用Path.glob()"**/"在给定的相对图案前面加:

import pathlib


for p in pathlib.Path("src").rglob("*.c"):
    print(p)
Run Code Online (Sandbox Code Playgroud)

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


Dan*_*iel 9

如果有人对此感兴趣,我已经介绍了最重要的三种建议方法。我在 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)
Run Code Online (Sandbox Code Playgroud)

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

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


Mil*_*vić 8

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)}')
Run Code Online (Sandbox Code Playgroud)
  • 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以当前目录和所有子目录结尾的所有文件


小智 6

最近我不得不用扩展名.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)
Run Code Online (Sandbox Code Playgroud)


And*_*ock 5

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

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


dav*_*ode 5

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

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

我真的很开心python :)


Sam*_*ami 5

对于 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)
Run Code Online (Sandbox Code Playgroud)

进一步你可能需要

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'
Run Code Online (Sandbox Code Playgroud)

  • 您的第一行代码不适用于查看子目录。但如果你只是通过 `/**` 扩展它,它对我有用,就像这样: `file_names_array = glob.glob('src/**/*.c', recursive=True)` (4认同)

Ped*_*ito 5

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

import glob
for x in glob.glob('path/**/*.c', recursive=True):
    print(x)
Run Code Online (Sandbox Code Playgroud)

演示版


如果递归为true,则该模式** 将匹配任何文件以及零个或多个directoriessubdirectories。如果模式后跟一个os.sep,则仅目录和subdirectories匹配项。

  • 在 Python 3.9.1 中,递归默认设置为 False。 (4认同)
  • 这比 pathlib.Path('./path/').glob('**/*') 效果更好,因为它在大小为 0 的文件夹中也是如此 (3认同)