如何使用glob.glob模块搜索子文件夹?

164

我想打开一个文件夹中的一系列子文件夹,找到一些文本文件并打印出文本文件的某些行。我正在使用以下代码:

configfiles = glob.glob('C:/Users/sam/Desktop/file1/*.txt')

但这个命令无法访问子文件夹。有人知道如何使用相同的命令来访问子文件夹吗?


1
使用Glob()在Python中递归查找文件 - samkhan13
1
这个回答是否解决了您的问题?如何使用glob()递归查找文件? - Basj
13个回答

247
在Python 3.5及更高版本中,使用新的递归**/功能:
configfiles = glob.glob('C:/Users/sam/Desktop/file1/**/*.txt', recursive=True)

当设置recursive时,路径分隔符后跟**将匹配0个或多个子目录。
在早期的Python版本中,glob.glob()不能递归地列出子目录中的文件。
在这种情况下,我会使用os.walk()结合fnmatch.filter()来代替。
import os
import fnmatch

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in fnmatch.filter(files, '*.txt')]

这将递归地遍历您的目录,并返回匹配.txt文件的所有绝对路径。在这种特定情况下,fnmatch.filter()可能过于复杂,您也可以使用.endswith()测试:

import os

path = 'C:/Users/sam/Desktop/file1'

configfiles = [os.path.join(dirpath, f)
    for dirpath, dirnames, files in os.walk(path)
    for f in files if f.endswith('.txt')]

4
我可以看到:glob.glob('/路径//.txt') 对我有用。这基本上使用了Unix shell规则。 - Surya
10
@User123 提到该命令没有递归列出目录。你列出了所有一级深度下的文本文件,但没有列出进一步子目录中的或直接在目录路径中的文本文件。 - Martijn Pieters
2
这不是完全相关的问题,但为什么在使用**/功能时将recursive=False设置为false无法提供给定文件夹中的文件列表,而是其子文件夹中的文件? - Dr_Zaszuś
1
@Dr_Zaszuś:如果您需要所有子目录中的所有文件,请使用 */* - Martijn Pieters
“递归”很令人困惑,我以为它可以遍历当前目录及其所有子目录。 - CodingNinja
显示剩余4条评论

83

这个主题存在很多困惑。让我来澄清一下(Python 3.7):

  1. glob.glob('*.txt') :匹配当前目录中所有以“.txt”结尾的文件
  2. glob.glob('*/*.txt') :同上
  3. glob.glob('**/*.txt') :只匹配直接子目录中以“.txt”结尾的文件,不包括当前目录
  4. glob.glob('*.txt',recursive=True) :同第1条
  5. glob.glob('*/*.txt',recursive=True) :同第3条
  6. glob.glob('**/*.txt',recursive=True):匹配当前目录和所有子目录中以“.txt”结尾的文件

因此,最好总是指定 recursive=True


2
我不确定第三种情况是否正确。我尝试使用pathlib.Path.glob和模式返回与第六种情况相同的结果(递归所有txt文件)。这也是当前最受欢迎的答案所提到的(https://dev59.com/z2Uq5IYBdhLWcg3wF8hQ#14798263)。 - Joe

26

查找直接子目录中的文件:

configfiles = glob.glob(r'C:\Users\sam\Desktop\*\*.txt')

如果需要递归遍历所有子目录的递归版本,你可以使用 ** 并传递参数 recursive=True 自 Python 3.5 开始

configfiles = glob.glob(r'C:\Users\sam\Desktop\**\*.txt', recursive=True)

两个函数调用都返回列表。您可以使用glob.iglob()逐个返回路径。或者使用pathlib:

from pathlib import Path

path = Path(r'C:\Users\sam\Desktop')
txt_files_only_subdirs = path.glob('*/*.txt')
txt_files_all_recursively = path.rglob('*.txt') # including the current dir

这两种方法都会返回迭代器(你可以逐个获取路径)。


是的,我理解了;但我也没想到glob()会支持目录中的模式匹配。 - Martijn Pieters
评论已删除,我现在明白它给人留下了错误的印象;此外,该补丁包括对**递归情况的文档更新。但是为了使**正常工作,你必须设置recursion=True开关。 - Martijn Pieters

17

glob2包支持通配符并具有相当快的速度

code = '''
import glob2
glob2.glob("files/*/**")
'''
timeit.timeit(code, number=1)

在我的笔记本电脑上,匹配超过60,000个文件路径大约需要2秒钟。


9
您可以在Python 2.6中使用 Formic
import formic
fileset = formic.FileSet(include="**/*.txt", directory="C:/Users/sam/Desktop/")

声明 - 我是这个软件包的作者。


4
这是一个改进版,可以在不使用glob2的情况下实现类似于glob.glob的功能。
def find_files(directory, pattern='*'):
    if not os.path.exists(directory):
        raise ValueError("Directory not found {}".format(directory))

    matches = []
    for root, dirnames, filenames in os.walk(directory):
        for filename in filenames:
            full_path = os.path.join(root, filename)
            if fnmatch.filter([full_path], pattern):
                matches.append(os.path.join(root, filename))
    return matches

如果您有以下目录结构:

tests/files
├── a0
│   ├── a0.txt
│   ├── a0.yaml
│   └── b0
│       ├── b0.yaml
│       └── b00.yaml
└── a1

您可以像这样做:

您可以采取以下措施

files = utils.find_files('tests/files','**/b0/b*.yaml')
> ['tests/files/a0/b0/b0.yaml', 'tests/files/a0/b0/b00.yaml']

基本上,fnmatch模式匹配整个文件名本身,而不仅仅是文件名。

4

对于当然已经在其他答案中提到的第一种选项,这里的目标是展示glob在内部使用了os.scandir,并直接给出这个答案。


使用glob

如前所述,使用Python 3.5+很容易:

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

#d:\temp\New folder
#d:\temp\New Text Document - Copy.txt
#d:\temp\New folder\New Text Document - Copy.txt
#d:\temp\New folder\New Text Document.txt

使用pathlib

from pathlib import Path
for f in Path('d:/temp').glob('**/*'):
    print(f)

使用os.scandir

os.scandirglob 内部所使用的操作。因此以下是如何直接使用 yield 来实现:

def listpath(path):
    for f in os.scandir(path):
        f2 = os.path.join(path, f)
        if os.path.isdir(f):
            yield f2
            yield from listpath(f2)
        else:
            yield f2

for f in listpath('d:\\temp'):
    print(f)

3

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt')

这种情况下并不适用,建议使用glob2。

configfiles = glob2.glob('C:/Users/sam/Desktop/**/*.txt")

2
如果您可以安装glob2包...
import glob2
filenames = glob2.glob("C:\\top_directory\\**\\*.ext")  # Where ext is a specific file extension
folders = glob2.glob("C:\\top_directory\\**\\")

所有文件名和文件夹:
all_ff = glob2.glob("C:\\top_directory\\**\\**")  

2
如果你正在运行 Python 3.4 或更新版本,你可以使用 pathlib 模块。 Path.glob() 方法支持使用 ** 模式,意思是“这个目录以及所有子目录,递归地”。它返回一个生成器,为所有匹配的文件产生 Path 对象。
from pathlib import Path
configfiles = Path("C:/Users/sam/Desktop/file1/").glob("**/*.txt")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接