Python 2.7 - 使用 scandir 遍历所有子目录和 return 列表

Python 2.7 - Using scandir to traverse all sub-directories and return list

使用 Python 2.7 和 scandir,我需要向下遍历所有目录和子目录以及 return 目录列表。不是文件。路径中子目录的深度可能会有所不同。

我知道 os.walk,但我的目录有 200 万个文件,因此 os.walk 会变慢。

目前下面的代码适用于我,但我怀疑可能有更容易的 way/loop 来达到相同的结果,我想知道如何改进它。我的功能的局限性还在于它仍然受到我可以遍历子目录的深度的限制,也许这可以克服。

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

如果您有更好的替代方法来快速 returning 具有数百万个文件的路径中的所有目录和子目录,请告诉我。

您可以使用 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))

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

首先,为了避免 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 (especially on Linux) But as far as I know, there are no good Python wrappers for it. If you know ctypes, it's not that complicated to just call it; the hard part is coming up with a good design for exposing all of its features to Python (which, of course, you don't need to do). See my unfinished library on GitHub 来加快速度。


或者,您可能想使用 find (which uses fts under the covers), either driving it via subprocess,或者让它驱动您的脚本。


最后,您可能希望并行执行操作。如果您的文件系统是一个旧的笔记本电脑硬盘驱动器,而不是两个 SSD 和一个带有 high-end 控制器的 RAID 条带,这实际上可能会减慢速度而不是加快速度。所以一定要试试看,然后再投入太多。

如果您正在做 non-trivial 工作,您可能只需要一个线程排队等待您的工作人员处理的目录。

如果步行是重点,您需要同时拉动多个助行器。 concurrent.futures.ThreadPoolExecutor wraps things up may just be good enough out of the box, and it's dead simple. For maximum speed, you may need to manually queue things up and pull them in batches, shard the work by physical volume, etc., but probably none of that will be necessary. (If it is, and if you can muddle through reading Rust code, ripgrep 尽可能快地导航文件系统的方式。)

scandir supports a walk() function that includes the same optimizations of scandir() so it should be faster than 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() 更好(更简单、更可靠),所以如果适合您,请改用它!