如何使用递归查找文件?

2024-11-29 08:41:00
admin
原创
174
摘要:问题描述:我想递归列出目录中的所有文件。我目前有一个如下的目录结构:src/main.csrc/dir/file1.csrc/another-dir/file2.csrc/another-dir/nested/files/file3.c我尝试做了以下事情:from glob import glob glob...

问题描述:

我想递归列出目录中的所有文件。我目前有一个如下的目录结构:

  • src/main.c

  • src/dir/file1.c

  • src/another-dir/file2.c

  • src/another-dir/nested/files/file3.c

我尝试做了以下事情:

from glob import glob

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

但这只能直接获取src子文件夹中的文件,例如我得到了main.c但我不会得到file1.cfile2.c等等。

from glob import glob

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

但这显然是有限和笨重的,我该如何正确地做到这一点?


解决方案 1:

有以下几种方法:

pathlib.Path().rglob()

pathlib.Path().rglob()pathlibPython 3.5 中引入的模块中使用。

from pathlib import Path

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

glob.glob()

如果你不想使用 pathlib,请使用glob.glob()

from glob import glob

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

对于匹配以点 ( .) 开头的文件的情况,例如当前目录中的文件或基于 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))

此版本的速度也应该更快,具体取决于您拥有的文件数量,因为 pathlib 模块比 有一点开销os.walk()

解决方案 2:

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

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

如果递归为True(默认False),则模式** 将匹配任何文件和零个或多个directoriessubdirectories。如果模式后跟os.sep,则只有目录和subdirectories匹配。


Python 3 演示

解决方案 3:

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

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

解决方案 4:

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

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

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

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

解决方案 5:

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

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语义非常接近。迭代版本(例如生成器),换句话说, 的替代品glob.iglob,是一种简单的改编(只需yield中间结果,而不是extend在最后返回单个结果列表)。

解决方案 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 和 Bruno 的示例使用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 的相关帖子以及其他地方的类似帖子。

解决方案 10:

import os, glob

for each in glob.glob('path/**/*.c', recursive=True):
    print(f'Name with path: {each} 
Name 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:

如果有人对此感兴趣,我已经介绍了三种最热门的方法。我的 glob 文件夹中有大约 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.5秒

iglob:~16.9秒**

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

解决方案 12:

最近,我不得不恢复带有 .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)

解决方案 13:

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

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

我真的很喜欢使用 Python:)

解决方案 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'

解决方案 15:

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

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

解决方案 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:Userxtofl"
 >>> binfiles = [ os.path.join(base,f) 
            for base, _, files in os.walk(root) 
            for f in files if f.endswith(".jpg") ] 

解决方案 18:

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

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

解决方案 19:

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

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

解决方案 20:

其中使用 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')])

解决方案 21:

除了建议的答案之外,您还可以使用一些惰性生成和列表理解魔法来实现此目的:

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

解决方案 22:

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

希望这能帮助那些遇到类似问题的人。

解决方案 23:

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

解决方案 24:

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

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

解决方案 25:

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

它用于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:

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)

解决方案 27:

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

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

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**    %s    %s    %s    %s" % (rootdir, filename, lastmod, creation, filesize)

searchdir = r'D:YourDirectoryRoot'
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))

解决方案 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找不到任何匹配的文件,您可能需要一些异常处理。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用