如何在 Python 中递归扫描目录?

2025年1月5日 | 15 分钟阅读

扫描目录是编程中的一项常见任务,尤其是在处理文件管理或数据处理时。Python 提供了多种遍历目录的方法,其中一种常见的方法是递归目录遍历。

递归目录遍历涉及访问目录树中的每个目录,包括所有子目录及其内容。当您需要搜索特定文件、对文件执行操作或仅仅是探索目录结构时,此技术非常有用。

Python 的 os 模块和 os.path 子模块提供了与文件系统交互的函数,包括目录遍历函数。os.walk() 函数对于递归目录遍历特别有用。

目录就像您计算机上的文件夹。它可以包含其他文件夹(称为子目录)和单个文件。当您将这些文件夹和文件组织在一个主文件夹内时,它被称为目录层次结构或树状结构。

想象一个家谱,但不是关于人的,而是关于文件夹和文件的。您可以浏览这个树来找到您需要的东西。

有几种方法可以浏览目录

  • 使用 os.walk() 方法:此方法可帮助您逐步遍历目录及其内容。
  • 使用 glob.glob() 方法:此方法可帮助您查找名称中具有特定模式的文件。
  • 使用 os.listdir() 方法:此方法列出目录中的所有文件和文件夹。

操作系统管理目录,所以如果您想检查目录的状态(例如它有多少文件),您可以使用 os 模块,它是操作系统工具的一部分。

使用 os.walk() 方法

os.walk() 函数是 Python 中的一个工具,可帮助您探索目录树中的文件夹和文件。您可以从顶部向下工作,也可以从底部向上工作。

当您使用 os.walk() 时,它会为您提供有关它找到的每个目录的信息。这些信息由三部分组成:目录的路径、子目录名称列表以及该目录中的文件名列表。

以下是每个部分的含义

  • “路径”只是计算机上目录的位置,以文本字符串表示。
  • “名称”是您当前查看的目录中所有文件夹的名称。它不包括像 '.' 或 '..' 这样的特殊文件夹,它们分别表示当前目录和父目录。
  • “文件名”是您当前查看的目录中所有文件的名称。它不包括任何文件夹。

让我们看一个如何使用 os.walk() 查看当前目录中所有文件和文件夹的示例。

示例 1

代码

输出

. ['.config', 'sample_data'] []
./.config ['logs', 'configurations'] ['default_configs.db', 'config_sentinel', '.last_opt_in_prompt.yaml', '.last_update_check.json', 'gce', 'active_config', '.last_survey_prompt.yaml']
./.config/logs ['2024.05.07'] []
./.config/logs/2024.05.07 [] ['13.20.55.184705.log', '13.21.40.438409.log', '13.21.20.276670.log', '13.21.51.555413.log', '13.21.31.385244.log', '13.21.52.237191.log']
./.config/configurations [] ['config_default']
./sample_data [] ['anscombe.json', 'README.md', 'california_housing_train.csv', 'mnist_train_small.csv', 'mnist_test.csv', 'california_housing_test.csv']

代码说明

此 Python 代码使用 os.walk() 函数遍历目录树。让我们一步一步地分解它

  1. import os: 此行导入 Python os 模块,该模块提供了一种与操作系统交互的方法。
  2. path = ".":此行将 path 变量设置为 ".",它代表当前目录。您可以将 "." 更改为您想要遍历的任何其他目录路径。
  3. for root, d_names, f_names in os.walk(path):此行启动一个 for 循环,该循环迭代 os.walk(path) 的结果。os.walk() 函数通过自顶向下或自底向上遍历树来生成目录树中的文件名。
  4. root:此变量保存当前正在遍历的目录。
  5. d_names:这是当前目录中目录名称的列表。
  6. f_names:这是当前目录中文件名列表。
  7. print(root, d_names, f_names):在循环内,此行打印当前目录 (root)、子目录名称列表 (d_names) 以及该目录中的文件名列表 (f_names)。

因此,当您运行此代码时,它将从指定路径开始打印目录结构,包括每个目录中的所有子目录和文件。

示例 2

此外,我们还可以为每个文件创建一个完整路径。我们必须为此使用 os.path.join() 函数。使用此技术将创建文件的路径。如以下所示,可以使用 append() 函数将这些路径从每个文件追加在一起。

代码

输出

fname = []

代码说明

此 Python 代码利用 os 模块遍历目录并收集其中所有文件的名称。

以下是代码各部分的说明

  • import os:此行导入 os 模块,该模块提供了一种与操作系统交互的方法,包括操作文件路径和目录的函数。
  • path = "./TEST":此行定义变量 path 并将其赋值为 "./TEST"。这是代码将搜索文件的目录路径。./ 表示当前目录,因此 "./TEST" 指的是当前目录中名为 "TEST" 的目录。
  • fname = []:此行初始化一个名为 fname 的空列表。此列表将用于存储在目录遍历期间找到的文件名。
  • for root, d_names, f_names in os.walk(path):此行使用 os.walk() 函数遍历由 path 指定的目录。os.walk() 通过自顶向下或自底向上遍历树来生成目录树中的文件名。它为遍历的每个目录生成一个元组,其中包含目录路径、子目录名称列表和该目录中的文件名列表。
  • for f in f_names:此行迭代当前正在遍历的目录中找到的文件名列表 (f_names)。
  • fname.append(os.path.join(root, f)):此行通过将目录路径 (root) 和文件名 (f) 连接起来,构建每个文件的完整路径。os.path.join() 函数用于确保使用当前操作系统正确的目录分隔符。然后,它将完整路径附加到 fname 列表中。
  • print("fname = %s" %fname):最后,此行打印在目录遍历期间收集的文件名列表 (fname)。

总之,此代码递归遍历由 path 指定的目录,收集其中所有文件的名称,并打印文件名列表。

示例 3

我们也可以使用 os.walk() 函数来选择要打印的返回元组的成员。让我们研究下面的示例程序。

代码

输出

.
./.config
./.config/logs
./.config/logs/2024.05.09
./.config/configurations
./sample_data
['.config', 'sample_data']
['logs', 'configurations']
['2024.05.09']
[]
[]
[]
[]
['default_configs.db', '.last_update_check.json', 'active_config', '.last_opt_in_prompt.yaml', '.last_survey_prompt.yaml', 'gce', 'config_sentinel']
[]
['13.24.23.617960.log', '13.24.13.774530.log', '13.23.50.356879.log', '13.24.31.258228.log', '13.24.42.436499.log', '13.24.41.868001.log']
['config_default']
['README.md', 'anscombe.json', 'california_housing_train.csv', 'mnist_test.csv', 'mnist_train_small.csv', 'california_housing_test.csv']

代码说明

此 Python 代码片段利用 os.walk() 函数遍历当前目录 (".") 中的目录和文件。以下是每个部分的作用的细分

  • import os:此行导入 os 模块,该模块提供了一种与操作系统交互的方法。它包含各种用于处理文件、目录和进程的函数。
  • for dirpath, dirs, files in os.walk("."):此行启动一个循环,该循环迭代 os.walk(".") 的结果。os.walk() 函数通过自顶向下或自底向上遍历树来生成目录树中的文件名。
  • dirpath:表示当前正在遍历的目录。
  • dirs:包含当前目录中子目录(文件夹)的列表。
  • files:包含当前目录中文件的列表。
  • print(dirpath):此行打印当前目录路径。
  • print(dirs):此行打印当前目录中的子目录列表。
  • print(files):此行打印当前目录中的文件列表。

因此,当执行代码时

  • 第一个循环打印所有目录路径。
  • 第二个循环打印每个目录中的所有子目录。
  • 第三个循环打印每个目录中的所有文件。

此代码有助于遍历目录和检查其内容,这对于文件管理、搜索或处理等各种任务非常有用。

使用 glob.glob() 方法

glob 模块就像一个搜索工具,用于在特定文件夹中查找与某个模式匹配的文件或文件夹。我们使用一个名为 glob.glob() 的方法来执行此搜索。

如果我们使用星号 (*) 作为模式,它表示“匹配任何内容”,因此该方法将找到该文件夹中的所有文件。

示例 4

假设我们想查看主文件夹中的所有文件和文件夹。我们可以使用 glob() 方法来完成此操作。方法如下

代码

输出

config
sample_data

代码说明

此代码是用 Python 编写的,并使用了 pathlib 模块,该模块提供了面向对象的接口来处理文件系统路径。让我们分解一下每个部分的作用

  • from pathlib import Path:此行从 pathlib 模块导入 Path 类。Path 表示文件系统路径,并提供了各种处理路径和文件的方法。
  • root_directory = Path('.'):此行创建了一个 Path 对象,表示当前目录 ('.')。Path('.') 调用构造了一个 Path 对象,表示脚本执行所在当前目录的路径。
  • size = 0:此行将变量 size 初始化为 0。此变量将用于存储目录中文件的总大小。
  • root_directory.glob("*"):此部分使用 Path 对象的 glob() 方法来生成当前目录 ('.') 中所有项目(文件、目录等)的可迭代对象。传递给 glob() 的 "*" 是一个匹配所有项目的通配符。
  • for f in ...:此部分迭代 glob("*") 生成的可迭代对象中的每个项目 (f)。
  • print(f):在循环内,此行打印目录中的每个项目 (f)。对于文件,它会打印它们的名称;对于目录,它也会打印它们的名称。

因此,此代码基本上列出了脚本执行所在当前目录中的所有文件和目录。

使用 os.listdir() 方法

os.listdir() 是 Python 标准库的 os 模块提供的一个方法。它返回一个列表,其中包含由路径指定的目录中条目的名称。条目以任意顺序返回。

os.listdir() 方法是列出 Python 目录内容的简单直接的方法。它返回指定目录路径中包含的文件名(或目录名)的列表。当您需要文件或目录的基本列表而无需任何其他处理时,通常会使用此方法。

示例 5

以下是一个演示其用法的示例代码片段

代码

输出

.config
sample_data

代码说明

此 Python 代码片段使用 os 模块列出当前目录 ('.') 中的所有文件和目录。让我们逐行分解

  • import os:此行导入 os 模块,该模块提供了一种与操作系统交互的方法,包括用于文件和目录操作的函数。
  • directory_path = '.':此行将字符串 '.' 赋值给变量 directory_path。在此上下文中,'.' 表示当前目录。
  • directory_contents = os.listdir(directory_path):此行使用 os 模块中的 listdir() 函数获取当前目录 ('.') 中所有文件和目录的列表。结果存储在 directory_contents 变量中。
  • for item in directory_contents:此行启动一个循环,该循环迭代 directory_contents 列表中的每个项目(文件或目录)。
  • print(item):此行打印 directory_contents 列表中的每个项目。它将每行打印一个项目,显示当前目录中存在的文件和目录的名称。

因此,当您运行此代码时,它将列出当前目录中的所有文件和目录。

递归扫描目录的一些优点

递归扫描 Python 中的目录具有多种优势

  1. 遍历嵌套目录
    当您有一个包含文件的多个嵌套目录的目录结构时,手动访问每个文件可能非常耗时且容易出错。Python 递归遍历目录的能力意味着您可以编写自动导航目录树的代码,在每个级别访问文件和目录,而无需事先知道它们的确切结构。这使得处理大型复杂目录层次结构更加容易。
  2. 自动化和批量处理
    编程中的许多任务都涉及处理目录或其子目录中的多个文件。例如,您可能需要调整文件夹及其子文件夹中所有图像的大小,或处理分布在不同目录中的日志文件。通过递归扫描目录,您可以自动化这些任务,节省时间和精力。这对于需要对大量文件应用相同操作的批量处理任务特别有用。
  3. 灵活性和可扩展性
    Python 的递归目录扫描功能高度灵活且可扩展。无论您是处理小型项目还是具有广泛目录结构的大型应用程序,Python 都提供了高效的方法来遍历目录,而不会带来显著的性能开销。这种灵活性允许您的代码适应不断变化的需求,并适应各种复杂性的目录结构。
  4. 动态文件处理
    Python 的动态类型和运行时解释使您能够动态处理不同类型的文件和目录。这意味着您的代码可以处理具有不同格式、扩展名和属性的文件,而无需显式类型声明。因此,您可以构建可以适应各种文件系统和数据源的通用应用程序。
  5. 跨平台兼容性
    Python 用于文件和目录操作的标准库模块,如 os 和 pathlib,旨在跨不同操作系统无缝工作。无论您是在 Windows、macOS、Linux 还是其他平台上开发,您的代码都可以可靠地遍历目录和访问文件,而无需进行特定于平台的修改。这种跨平台兼容性确保了可移植性并减少了开发开销。
  6. 自定义和过滤
    Python 中的递归目录扫描提供了广泛的自定义选项,允许您根据特定标准过滤文件。例如,您可以根据文件的扩展名、大小、创建日期或其他元数据属性选择性地处理文件。这种灵活性使您能够根据应用程序的需求定制目录遍历过程,确保高效且有针对性的文件处理。
  7. 错误处理
    Python 提供了强大的错误处理机制,包括 try-except 块和异常处理,这对于处理目录遍历期间的潜在错误至关重要。无论是处理权限问题、文件未找到错误,还是在文件操作期间可能发生的其他异常,Python 都允许您优雅地处理这些情况,防止意外崩溃并确保应用程序的健壮性。

通过利用这些优势,Python 开发人员可以创建功能强大且高效的应用程序,用于文件管理、数据处理、自动化和系统管理等任务。递归目录扫描是许多 Python 项目中的基础功能,它使开发人员能够以灵活、可扩展且可靠的方式处理文件和目录。

递归扫描 Python 中的目录的一些缺点

在 Python 中递归扫描目录可能是一项有用的操作,但它也有一些缺点

  1. 性能
    递归目录扫描涉及遍历每个目录和子目录,这可能非常耗时,尤其是当目录结构很大时。这可能导致执行时间变慢,尤其是在处理包含大量文件或嵌套子目录的目录时。
  2. 资源消耗
    随着扫描递归地通过目录,它会消耗内存和 CPU 资源。这在资源有限的系统上或扫描大型目录树时可能会出现问题,可能导致性能下降甚至系统减速。
  3. 文件系统限制
    各种文件系统都有限制,例如最大文件路径长度或最大目录深度。在递归扫描目录时,需要考虑这些限制,以避免错误或意外行为。例如,在 Windows 上,许多应用程序的最大路径长度为 260 个字符,超过此限制可能会导致问题。
  4. 错误处理
    递归目录扫描需要健壮的错误处理来处理各种情况,例如权限被拒绝错误、符号链接或无法访问的目录。未能正确处理这些错误可能导致程序崩溃或扫描结果不完整。
  5. 无限递归的可能性
    递归函数必须有一个终止条件,以避免无限递归。但是,在目录扫描中,如果处理不当,符号链接或循环引用有时可能导致意外的无限递归。这可能导致资源消耗过多和程序崩溃。
  6. 安全风险
    当递归扫描包含敏感信息或具有不当权限的文件的目录时,存在无意中暴露这些数据的风险。必须注意确保扫描过程不损害安全或隐私。
  7. 平台依赖性
    不同操作系统上的目录结构和文件系统可能有所不同。因此,在一个平台上运行正常的代码在另一个平台上可能会表现不同或遇到问题。开发人员需要了解这些平台差异,并编写健壮且可跨不同环境移植的代码。
  8. 控制有限
    递归目录扫描可能无法提供对文件和目录处理顺序的精细控制。在需要特定排序或优先级的文件/目录以实现高效处理的情况下,这可能是一个限制。

为了解决这些缺点,开发人员可以采用各种策略,例如优化扫描算法以获得更好的性能,实现健壮的错误处理机制,确保平台兼容性,并纳入安全最佳实践。此外,使用 os、os.path 或 Pathlib 等库,它们提供文件和目录操作的高级抽象,可以简化扫描过程并缓解其中一些挑战。

递归扫描 Python 中的目录的各种应用

Python 中的递归目录扫描是一项常见任务,尤其是在文件管理、数据处理和自动化应用程序中。以下是在 Python 中递归扫描目录的一些常见应用

  1. 文件管理
    假设您正在构建一个文件同步工具。您需要比较不同目录中的文件,以确定哪些文件已修改并需要同步。通过递归扫描目录,您可以有效地识别所有文件及其路径,从而更轻松地进行比较和同步。
  2. 数据处理
    想象一下,您正在处理一个涉及分析存储在嵌套目录中的文本文件的项目。通过递归扫描目录,您可以访问所有相关文件,处理其内容,并执行情感分析、关键字提取或语言翻译等任务。
  3. 备份和同步
    在备份应用程序中,您需要识别所有要包含在备份过程中的文件和目录。递归目录扫描可让您遍历整个目录结构,确保在备份或同步操作期间不会遗漏任何文件。
  4. 搜索和索引
    搜索引擎使用目录扫描来索引存储在文件系统上的网页或文档。通过递归遍历目录,您可以构建所有文件及其内容的索引,从而实现快速高效的搜索功能。
  5. 构建系统
    考虑一个软件构建系统,它编译存储在多个目录中的源代码。递归目录扫描有助于查找构建过程所需的所有相关源文件、依赖项和资源,从而简化软件的编译和打包。
  6. 测试
    测试框架通常需要查找散布在不同目录中的测试文件。递归目录扫描可让您自动发现并执行所有相关的测试用例,确保对软件项目进行全面的测试覆盖。
  7. 安全扫描
    安全工具需要扫描目录以查找潜在的恶意文件或可疑活动。通过递归遍历目录,您可以分析文件内容,检测恶意软件,并识别可能对系统构成威胁的安全漏洞。
  8. 数据爬取
    网络爬虫和数据抓取工具通常需要从各种来源下载和存储文件。递归目录扫描有助于将下载的文件组织到结构化的目录层次结构中,从而更轻松地管理和处理收集到的数据。
  9. 版本控制系统
    像 Git 这样的版本控制系统使用递归目录扫描来跟踪文件和目录中的更改。通过递归遍历工作目录,Git 可以识别修改、添加或删除的文件,使开发人员能够有效地管理和协作编写代码。
  10. 文件系统实用程序
    各种文件系统实用程序依赖递归目录扫描来执行磁盘空间分析、文件类型检测和重复文件检测等任务。通过递归遍历目录,这些实用程序可以收集文件属性信息,识别冗余文件,并对文件系统执行维护任务。

总而言之,Python 中的递归目录扫描是一种多功能工具,在文件管理、数据处理、安全和系统管理等广泛领域都有应用。通过了解如何实现和自定义目录扫描功能,您可以有效地满足 Python 项目中的各种需求。

结论

总之,在 Python 中递归扫描目录是一种强大而通用的技术,可以有效地导航和处理文件系统。通过利用 os 或 os.path 等库,以及内置的 os.walk() 函数或 pathlib 等第三方库,开发人员可以创建健壮的解决方案,用于从简单的文件列表到复杂的数据处理管道的各种任务。