关于python:如何使用glob()递归查找文件?

How to use glob() to find files recursively?

这就是我所拥有的:

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

但我想搜索SRC的子文件夹。类似这样的事情会奏效:

1
2
3
4
glob(os.path.join('src','*.c'))
glob(os.path.join('src','*','*.c'))
glob(os.path.join('src','*','*','*.c'))
glob(os.path.join('src','*','*','*','*.c'))

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


Python 3.5 +

从python 3.5版开始,glob模块支持"**"指令(仅当传递recursive标志时才解析该指令):

1
2
3
4
import glob

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

如果需要列表,只需使用glob.glob而不是glob.iglob

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

python 2.2到3.4

对于较旧的python版本,从python 2.2开始,使用os.walk递归地遍历目录,使用fnmatch.filter与简单表达式进行匹配:

1
2
3
4
5
6
7
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及更早版本

对于更旧的python版本,对每个文件名使用glob.glob,而不是fnmatch.filter


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

1
2
3
4
5
6
7
8
9
10
11
12
13
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

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


我已经修改了glob模块以支持**递归的globbing,例如:

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

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

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


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

1
2
3
4
from pathlib import Path

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

更新:从Python3.5开始,glob.glob()也支持相同的语法。


1
2
3
4
5
6
7
8
9
10
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的方法,语义非常接近。迭代版本(例如,生成器)缺少对glob.iglob的替换,这是一个微不足道的修改(只是yield中间结果,而不是extend在末尾返回单个结果列表)。


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

1
2
3
4
5
6
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))

这里有一个嵌套列表理解的解决方案,os.walk和简单的后缀匹配,而不是glob

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

它可以压缩成一个内衬:

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

或概括为一个函数:

1
2
3
4
5
6
7
8
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的和布鲁诺的例子和用法

1
2
3
4
5
6
7
8
9
10
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')

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

1
2
3
4
5
6
7
8
9
10
#!/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)

Johan和Bruno在规定的最低要求上提供了出色的解决方案。我刚刚发布了formic,它实现了Ant文件集和globs,可以处理这个和更复杂的场景。您的要求的实现是:

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


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

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

我真的很喜欢Python:)


另一种方法是只使用glob模块。只需在rglob方法中输入一个起始基目录和要匹配的模式,它将返回一个匹配文件名的列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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

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

1
2
3
4
5
6
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文件。


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

但我没有用火柴或走路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/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)

或者用清单理解:

1
2
3
4
 >>> 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") ]


使用fnmatch或正则表达式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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')])

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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

没有fnmatch的johan dahlin答案的简化版本。

1
2
3
4
5
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']

我需要一个能在大目录下快速运行的python2.x解决方案。我以这个结尾:

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
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)


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

希望这能帮助别人…他们可以浏览目录并获取文件信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
oot'

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
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