Python 2.7 - 使用scandir遍历所有子目录并返回列表

时间:2018-04-05 05:07:41

标签: python directory-structure subdirectory scandir

使用Python 2.7和scandir,我需要遍历所有目录和子目录,并返回目录列表。不是文件。沿路径的子目录深度可能会有所不同。

我知道os.walk,但是我的目录有200万个文件,因此os.walk的速度很慢。

目前下面的代码对我有用,但我怀疑可能有更简单的方法/循环来实现相同的结果,我想知道它是如何改进的。我的功能的局限性在于它仍然受到我可以遍历到子目录的深度的限制,并且可能这可以克服。

def list_directories(path):
dir_list = []
for entry in scandir(path):
    if entry.is_dir():
        dir_list.append(entry.path)
        for entry2 in scandir(entry.path):
            if entry2.is_dir():
                dir_list.append(entry2.path)
                for entry3 in scandir(entry2.path):
                    if entry3.is_dir():
                        dir_list.append(entry3.path)
                        for entry4 in scandir(entry3.path):
                            if entry4.is_dir():
                                dir_list.append(entry4.path)
                                for entry5 in scandir(entry4.path):
                                    if entry5.is_dir():
                                        dir_list.append(entry5.path)
                                        for entry6 in scandir(entry5.path):
                                            if entry6.is_dir():
                                                dir_list.append(entry6.path)
return dir_list
for item in filelist_dir(directory):
    print item

如果您有更好的替代方法,请在包含数百万个文件的路径中快速返回所有目录和子目录,请告知我们。

3 个答案:

答案 0 :(得分:1)

scandir支持walk()函数,该函数包含相同的scandir()优化,因此它应该比os.walk()更快。 (scandir' s background section建议在Linux / Mac OS X上进行3到10倍的改进。)

所以你可以使用它......像这样的代码可能有用:

from scandir import walk

def list_directories(path):
    dir_list = []
    for root, _, _ in walk(path):
        # Skip the top-level directory, same as in your original code:
        if root == path:
            continue
        dir_list.append(root)
    return dir_list

如果你想使用scandir()来实现它,为了实现支持任意深度的东西,你应该使用递归。

类似的东西:

from scandir import scandir

def list_directories(path):
    dir_list = []
    for entry in scandir(path):
        if entry.is_dir() and not entry.is_symlink():
            dir_list.append(entry.path)
            dir_list.extend(list_directories(entry.path))
    return dir_list

注意:我也添加了对is_symlink()的检查,因此它不会遍历符号链接。否则,符号链接指向'。'或者' ..'会让这个永远得到这个...... ...

我仍然认为使用scandir.walk()更好(更简单,更可靠),所以如果这适合你,请改用它!

答案 1 :(得分:0)

首先,要避免6个目录的限制,您可能希望以递归方式执行此操作:

def list_directories(path):
    dir_list = []
    for entry in scandir(path):
        if entry.is_dir():
            dir_list.append(entry.path)
            dir_list.extend(list_directories(entry.path))

此外,由于您使用的是Python 2.7,因此os.walk太慢的部分原因是Python 2.7使用listdir代替scandir walkscandir backport package包含自己的walk实现(基本上与Python 3.5中使用的实现相同),它提供与walk相同的API但具有较高的加速(特别是在Windows上)。

除此之外,您的主要性能成本可能取决于平台。

在Windows上,主要是读取目录条目的成本。你可以做的事情真的不多; scandir已经以最快的方式做到了这一点。

在POSIX上,它可能主要是stat每个文件的成本,看它是否是一个目录。您可以使用fts(特别是在Linux上)来加快速度。但据我所知,没有好的Python包装器。如果您知道ctypes,那么打电话就不那么复杂了;困难的部分是提出一个好的设计,将其所有功能暴露给Python(当然,你不需要这样做)。如果您想亲自尝试,请参阅my unfinished library on GitHub

或者,您可能希望使用find(在封面下使用fts),或者通过subprocess推送,或让它驱动您的脚本。

最后,你可能想要并行做事。如果您的文件系统是旧的笔记本电脑硬盘驱动器,而不是两个SSD和带有高端控制器的RAID条带,这实际上可能会减慢速度,而不是加快速度。所以一定要尝试一下,然后再做太多的事情。

如果您正在进行非平凡的工作,可能只需要一个单独的步骤线程,为您的工作人员排队目录。

如果走路是重点,那么你想要并行拉动多个步行者。 concurrent.futures.ThreadPoolExecutor包装起来的方式可能只是开箱即用,而且很简单。为了获得最大速度,您可能需要手动排队并将它们分批拉出,按物理卷等方式对工作进行分片,但可能没有必要。 (如果是,并且如果你可以通过阅读Rust代码混淆,ripgrep会尽可能快地导航文件系统。)

答案 2 :(得分:-1)

您可以使用python内置模块os.walk

for root, dirs, files in os.walk(".", topdown=False):
   for name in files:
      print(os.path.join(root, name))
   for name in dirs:
      #this will get your all directories within the path
      print(os.path.join(root, name))

有关详细信息,请访问此链接:os.walk