为命令行构建 Python 目录树生成器

举报
Yuchuan 发表于 2021/11/25 15:26:26 2021/11/25
【摘要】 您可以通过创建CLI工具和应用程序来自动化和加速工作环境中的多个流程和任务。在 Python 中,您可以使用argparse或其他第三方库快速创建此类工具。在本教程中,您编写了一个完整的项目来为命令行构建 Python目录树生成器工具。 该应用程序在命令行中获取目录路径,生成目录树图,并将其显示在终端窗口中或将其保存到文件系统上的外部文件中。它还提供了更多选项来调整生成的树图。

目录

使用用户友好的命令行界面 (CLI)创建应用程序是 Python 开发人员的一项有用技能。借助此技能,您可以创建工具来自动化和加快工作环境中的任务。在本教程中,您将为命令行构建一个 Python 目录树生成器工具。

该应用程序将在命令行中将目录路径作为参数,并在您的屏幕上显示目录树图。它还将提供其他选项来调整输出。

在本教程中,您将学习如何:

  • 使用 Python 的创建CLI 应用程序argparse
  • 递归遍历目录结构使用pathlib
  • 生成、格式化和显示目录树图
  • 将目录树图保存到输出文件

您可以通过单击下面的链接下载构建此目录树生成器项目所需的代码和其他资源:

演示:Python 中的目录树生成器工具

在本教程中,您将构建一个命令行工具以在树状图中列出目录或文件夹的内容。已经有几个成熟的解决方案可以执行此任务。您会找到像command这样的工具,它在大多数操作系统上都可用,以及其他工具,如treelibdirtriex等。但是,找出您自己的解决方案来解决这个问题将是一个很好的学习练习。tree

本教程将上述那种工具称为目录树生成器。您将在此处构建的工具将允许您生成并显示一个树状图,其中列出了文件系统中给定目录的内部结构。在整个教程中,您还会发现这个图被称为目录树图

您的目录树生成器将具有用户友好的 CLI。它还将提供一些有趣的功能,例如在终端窗口上显示带有目录内容的树形图,并将该图保存到外部文件中。

这是本教程结束后应用程序的外观和工作方式:

目录树生成器演示

您的目录树生成器将提供一个功能齐全但最小的 CLI 和几个选项,允许您生成和显示列出给定根目录中所有文件和目录的树图。

项目概况

您将在本教程中构建的项目由一个命令行应用程序组成,该应用程序将目录路径作为参数,遍历其内部结构,并生成一个列出手头目录内容的树状图。在本节中,您将首先了解问题和可能的解决方案。您还将决定如何布置项目。

布置项目

要构建您的目录树生成器,您将创建一些模块和一个包。然后,您将为项目提供一个连贯的 Python应用程序布局。在本教程结束时,您项目的根目录将具有以下目录结构:

./rptree_project/
│
├── rptree/
│   ├── rptree.py
│   ├── __init__.py
│   └── cli.py
│
├── README.md
└── tree.py

rptree_project/目录是项目的根目录。在那里,您将放置以下文件:

  • README.md提供有关安装和运行应用程序的项目描述和说明。将描述性和详细的README文件添加到您的项目被认为是编程的最佳实践,尤其是当您计划将项目作为开源解决方案发布时。

  • tree.py 为您提供了一个入口点脚本来运行应用程序。

然后你有rptree/一个包含三个模块的 Python 包的目录:

  1. rptree.py 提供应用程序的主要功能。
  2. __init__.pyrptree/作为 Python 包启用。
  3. cli.py 为应用程序提供命令行界面。

您的目录树生成器工具将在命令行上运行。它将接受参数,处理它们,并在终端窗口上显示目录树图。它还可以将输出图以降价格式保存到文件中。

概述解决方案

乍一看,遍历文件系统中的目录并生成反映其内容的用户友好的树形图可能不是一项艰巨的任务。但是,当您开始考虑它时,您会意识到它隐藏了很多复杂性。

首先,这是一个涉及递归的问题。假设您在主目录中打开了文件管理器,并且正在查找特定文件。然后双击Documents/子目录并在屏幕上显示其内容。如果文件在那里,那么你打开它。否则,您打开另一个子目录并继续查找。您可以通过以下步骤描述此过程:

  1. 打开一个目录。
  2. 检查目录内容。
  3. 如果找到该文件,请打开它。否则,返回第一步。

结论是,处理目录及其内容是您通常使用recursion处理的一个问题。这就是您将在本教程中遵循的路径。通常,您将运行以下步骤:

  1. 获取文件系统上目录的路径。
  2. 打开目录。
  3. 获取其所有条目(目录和文件)的列表。
  4. 如果目录包含子目录,则从第二步开始重复该过程。

要运行第一步,您需要为应用程序提供一种在命令行中获取目录路径的方法。为此,您将使用标准库中的Pythonargparse模块。

要完成第二步和第三步,您将使用pathlib. 该模块提供了多种工具来管理和表示文件系统路径。最后,您将使用常规 Python列表来存储目录结构中的条目列表。

要考虑的第二点是如何塑造一个美观的树形图,以准确和用户友好的方式反映目录结构。在本教程中,您将使用一种模仿tree命令功能的策略来塑造树形图,因此您的图将与您在上一节中看到的一样。

组织代码

设计方面,如果你想到手头的问题并应用单一职责原则,那么你可以根据三个主要职责来组织你的目录树生成器应用程序的代码:

  1. 提供 CLI
  2. 遍历根目录并构建树状图
  3. 显示树状图

与 CLI 相关的代码将位于cli.py. 在 中rptree.py,您将放置与第二个和第三个职责相关的代码。

在本例中,您将编写一个高级DirectoryTree类来生成和显示树形图。您将在客户端代码或main 函数中使用此类。该类将提供一个调用方法.generate()来生成和显示目录树图。

接下来,您将编写一个低级_TreeGenerator类来遍历目录结构并创建包含构成树状图的条目的列表。此类将提供一个调用方法.build_tree()来执行此操作。

树状图将有两个主要组成部分:

  1. Head将提供根目录表示。
  2. 正文将提供目录内容表示。

树头表示将由根目录的名称和连接树头和主体的附加管道 ( ) 字符组成。

树体表示将由包含以下组件的字符串组成:

  • 提供所需间距以反映条目在目录结构中的位置的前缀字符串
  • 连接当前子目录或文件与其父目录的字符
  • 当前子目录或文件的名称

以下是您将如何组合这些元素以构建目录树图的方法:

目录树图

您的树生成器的.build_tree()方法将返回一个列表,其中包含构成目录树图的所有条目。要显示图表,您需要调用.generate()目录树对象。

先决条件

要完成本教程并充分利用它,您应该熟悉以下概念:

  • 使用 Python 的argparse模块创建命令行界面 (CLI)
  • 遍历文件系统pathlib
  • 在 Python 中使用递归和创建递归函数
  • 处理文件usingopen()with语句
  • 使用print()打印文本到屏幕上,也写入物理文件在你的文件系统
  • 在 Python 中使用面向对象编程

如果您在开始本教程之前没有掌握所有必需的知识,那也没关系!您可以随时停下来查看以下资源:

在软件依赖方面,您的目录树生成器项目不需要任何外部库。它的所有依赖项都可以作为 Python 内置函数或作为标准库中的模块使用。

也就是说,是时候用真实的代码弄脏你的手并构建你自己的目录树生成器工具了!

步骤 1:设置项目结构

首先,您需要为目录树生成器项目创建一致的应用程序布局。继续在您的文件系统上创建一个名为 的新目录rptree_project/。在此目录中,您需要两个空文件:

  1. README.md
  2. tree.py

接下来,你需要创建一个名为的子目录rptree/,在它下面的空文件:rptree.py__init__.py,和- cli.py。添加后,您的项目的根目录应如下所示:

./rptree_project/
│
├── rptree/
│   ├── rptree.py
│   ├── __init__.py
│   └── cli.py
│
├── README.md
└── tree.py

要下载这些文件以及您将在本节中添加到其中的代码,请单击以下链接:

此时,您需要一个额外的设置步骤。在项目目录中启动您最喜欢的代码编辑器或 IDE,打开__init__.py并添加以下内容:

# __init__.py

"""Top-level package for RP Tree."""

__version__ = "0.1.0"

Python 使用__init__.py文件将普通目录转换为包。包包含的模块,如rptree.pycli.py此项目。包和模块是允许您组织和构建 Python 代码的机制。

在这种情况下,__init__.py包含模块的文档字符串,通常称为docstring。它还定义了一个名为的全局常量__version__,用于保存应用程序的版本号。

最后,您需要一个示例目录来测试应用程序并确保它正常工作。保留项目的根目录并在文件系统中创建以下目录结构,并与项目文件夹并排:

../hello/
│
├── hello/
│   ├── __init__.py
│   └── hello.py
│
├── tests/
│   └── test_hello.py
│
├── requirements.txt
├── setup.py
├── README.md
└── LICENSE

此目录结构模仿 Python 项目的总体布局。在本教程的整个步骤中,您将使用此示例目录结构来测试目录树生成器工具。这样,您可以在教程的任何给定步骤中将结果与预期结果进行比较。

第 2 步:在 Python 中生成目录树图

既然您已经了解了项目的要求,并且已经设置了项目布局和示例目录,您就可以开始处理真正的代码了。因此,让您的编辑器准备好开始编码。

现在回到您的代码编辑器并打开rptree.py. 然后将以下代码添加到文件中:

# rptree.py

"""This module provides RP Tree main module."""

import os
import pathlib

PIPE = "│"
ELBOW = "└──"
TEE = "├──"
PIPE_PREFIX = "│   "
SPACE_PREFIX = "    "

在这段代码中,您首先从 Python 标准库导入 ospathlib。接下来,您定义几个模块级常量来保存连接器字符和前缀字符串,您将使用它们在终端窗口上绘制树形图。您将用于绘制树状图的符号与您在本教程之前的图中看到的符号相同。命令行工具tree使用这些相同的符号来绘制树形图。

编写高级DirectoryTree

接下来,您将定义一个高级类来创建目录树图并将其显示在屏幕上。为类命名DirectoryTree并向其添加以下代码:

# rptree.py
# Snip...

class DirectoryTree:
    def __init__(self, root_dir):
        self._generator = _TreeGenerator(root_dir)

    def generate(self):
        tree = self._generator.build_tree()
        for entry in tree:
            print(entry)

在类初始化,你拿根目录作为参数,并创建一个实例属性._generator。要创建此属性,您可以使用称为组合的 OOP 技术,该技术定义了“具有”关系。这意味着每个DirectoryTree对象都有一个 _TreeGenerator附加对象。

注意:名称中的前导下划线字符 ( _)_TreeGenerator是常用的 Python 约定。这意味着该类是nonpublic,这意味着您不希望从其包含的模块 之外使用该类rptree.py

同样的约定适用于非公共方法和属性,您不希望在包含类的外部使用它们。通常,您开始将属性定义为非公共属性,并在需要时将它们设为公共属性。有关此约定的更多详细信息,请参阅PEP 8

您将_TreeGenerator在一分钟内看到如何创建这个类。现在,看看.generate()。此方法创建一个名为的局部变量tree,该变量保存调用.build_tree()树生成器对象的结果。然后使用for循环entry将树中的每个打印到屏幕上。

编码低级_TreeGenerator

现在您已经完成了编码DirectoryTree,是时候编写遍历文件系统并生成目录树图的类了:

 1# rptree.py
 2# Snip...
 3
 4class _TreeGenerator:
 5    def __init__(self, root_dir):
 6        self._root_dir = pathlib.Path(root_dir)
 7        self._tree = []
 8
 9    def build_tree(self):
10        self._tree_head()
11        self._tree_body(self._root_dir)
12        return self._tree
13
14    def _tree_head(self):
15        self._tree.append(f"{self._root_dir}{os.sep}")
16        self._tree.append(PIPE)

下面是这段代码的工作原理:

  • 第 4 行定义了一个新类_TreeGenerator

  • 第 5 行定义了类初始值设定项。在这种情况下,.__init__()root_dir作为参数。它保存树的根目录路径。请注意,您将root_dir变成一个pathlib.Path对象并将其分配给非公共实例属性._root_dir

  • 第 7 行定义了一个空列表来存储构成目录树图的条目。

  • 第 9 到 12 行定义了.build_tree(). 此公共方法生成并返回目录树图。在内部.build_tree(),您首先调用._tree_head()构建树头。然后你调用._tree_body()with._root_dir作为参数来生成图表的其余部分。

  • 第 14 到 16 行定义了._tree_head(). 此方法将根目录的名称添加到._tree. 然后添加一个PIPE将根目录连接到树的其余部分。

到目前为止,您只编写了类的第一部分。下一步是编写._tree_body(),这将需要几行代码。

注意:上述代码和本教程中其余代码示例中的行号旨在方便解释。它们与最终模块或脚本中的行顺序不匹配。

中的代码._tree_body()提供了类的低级功能。它以一个目录路径为参数,遍历该目录下的文件系统,生成相应的目录树图。这是它的实现:

 1# rptree.py
 2# Snip...
 3
 4class _TreeGenerator:
 5    # Snip...
 6
 7    def _tree_body(self, directory, prefix=""):
 8        entries = directory.iterdir()
 9        entries = sorted(entries, key=lambda entry: entry.is_file())
10        entries_count = len(entries)
11        for index, entry in enumerate(entries):
12            connector = ELBOW if index == entries_count - 1 else TEE
13            if entry.is_dir():
14                self._add_directory(
15                    entry, index, entries_count, prefix, connector
16                )
17            else:
18                self._add_file(entry, prefix, connector)

这段代码发生了很多事情。这是它的作用,一行一行:

  • 第 7 行定义了._tree_body(). 这个方法有两个参数:

    1. directory保存要遍历的目录的路径。注意directory应该是一个pathlib.Path对象。

    2. prefix保存用于在终端窗口上绘制树形图的前缀字符串。此字符串有助于显示目录或文件在文件系统中的位置。

  • 8部线电话.iterdir()directory,然后将结果来entries。此调用.iterdir()返回包含在directory.

  • 第 9 行directoryusing 中的条目进行排序sorted()。为此,您需要创建一个lambda函数来检查是否entry为文件并相应地返回True或返回False。在 Python 中,TrueFalse在内部分别表示为整数10。最终效果是sorted()将目录放在最前面,因为entry.is_file() == False == 0将文件放在后面,因为entry.is_file() == True == 1.

  • 第 10 行调用len()以获取directory手头的条目数。

  • 第 11 行开始一个for循环,它对 中的条目进行迭代directory。该循环用于enumerate()将索引与每个条目相关联。

  • 第 12 行定义了您将用于在终端窗口上绘制树状图的连接器符号。例如,如果当前条目是目录 ( index == entries_count - 1) 中的最后一个条目,那么您可以使用弯头 ( └──) 作为connector. 否则,您将使用 T 恤 ( ├──)。

  • 第 13 到 18 行定义了一个条件语句,用于检查当前条目是否为目录。如果是,则if代码块调用._add_directory()以添加新目录条目。否则,该else子句调用._add_file()以添加新文件条目。

要完成编码_TreeGenerator,您需要编写._add_directory()._add_file()。这是这些非公共方法的代码:

 1# rptree.py
 2# Snip...
 3
 4class _TreeGenerator:
 5    # Snip...
 6
 7    def _add_directory(
 8        self, directory, index, entries_count, prefix, connector
 9    ):
10        self._tree.append(f"{prefix}{connector} {directory.name}{os.sep}")
11        if index != entries_count - 1:
12            prefix += PIPE_PREFIX
13        else:
14            prefix += SPACE_PREFIX
15        self._tree_body(
16            directory=directory,
17            prefix=prefix,
18        )
19        self._tree.append(prefix.rstrip())
20
21    def _add_file(self, file, prefix, connector):
22        self._tree.append(f"{prefix}{connector} {file.name}")

下面是这段代码的作用,一行一行:

  • 第 7 行定义了._add_directory(). 这是一个辅助方法,它接受五个参数,不计算self。您已经知道每个参数代表什么,因此无需再次介绍它们。

  • 第 10行将一个新目录附加到._tree. 中的每个目录._tree都由包含 a prefix、 a connector、目录名称 ( entry.name) 和最终分隔符 ( os.sep)的字符串表示。请注意,分隔符是平台相关的,这意味着您的树生成器使用与您当前的操作系统相对应的分隔符。

  • 第 11 到 14 行运行一个条件语句,该语句prefix根据index当前条目的更新。

  • 第 15 到 18 行调用._tree_body()了一组新参数。

  • 第 19 行追加了一个 newprefix来将当前目录的内容与下一个目录的内容分开。

._tree_body()在第 15 行的调用中有一个重要的细节讨论。这是一个间接递归调用。换句话说,._tree_body()就是通过._add_directory()直到遍历整个目录结构来调用自己。

最后,在第 21 和 22 行,您定义._add_file(). 此方法将文件条目附加到目录树列表中。

运行目录树生成器代码

哇!那是很多工作!您的目录树生成器现在提供其主要功能。是时候试一试了。在项目的根目录上打开一个Python 交互式会话并键入以下代码:

>>>
>>> from rptree.rptree import DirectoryTree
>>> tree = DirectoryTree("../hello")
>>> tree.generate()
../hello/
│
├── hello/
│   ├── __init__.py
│   └── hello.py
│
├── tests/
│   └── test_hello.py
│
├── requirements.txt
├── setup.py
├── README.md
└── LICENSE

在这里,您首先DirectoryTreerptree.py. 接下来,您创建一个目录树对象,将路径传递到之前创建的hello/示例目录。当您调用.generate()目录树对象时,您会在屏幕上打印完整的目录树图。

凉爽的!您已经编写了目录树生成器的主要功能。在下一部分中,您将为您的项目提供一个漂亮且用户友好的命令行界面和一个可执行脚本。

第 3 步:构建目录树生成器的 CLI

有多种工具可用于创建 CLI 应用程序。一些比较流行的有ClickdocoptTyper以及argparse标准库中的 。在您的目录树生成器项目中,您将使用argparse提供命令行界面。这样,您将避免外部依赖。

Pythonargparse允许您定义应用程序将在命令行中采用的参数并验证用户的输入。该模块还为您的脚本生成帮助和使用消息。

要下载您将在此部分添加或修改的文件和代码,请单击以下链接:

要实现目录树生成器的 CLI,请返回项目目录并cli.pyrptree包中打开文件。然后输入以下代码:

"""This module provides the RP Tree CLI."""
# cli.py

import argparse
import pathlib
import sys

from . import __version__
from .rptree import DirectoryTree

def main():
    args = parse_cmd_line_arguments()
    root_dir = pathlib.Path(args.root_dir)
    if not root_dir.is_dir():
        print("The specified root directory doesn't exist")
        sys.exit()
    tree = DirectoryTree(root_dir)
    tree.generate()

在这段代码中,您首先从标准库中导入所需的模块。然后您导入__version__并且也DirectoryTree从包含的包中导入rptree.

在 中main(),您首先在 中调用parse_cmd_line_arguments()并打包命令行参数args。您将在一分钟内看到此函数的作用。接下来,将根目录转换为pathlib.Path对象。条件语句进行快速验证以确保用户提供有效的目录路径,否则退出应用程序。

最后,您创建一个用作参数的DirectoryTree对象root_dir并调用.generate()它以在终端窗口上生成和显示相应的目录树图。

现在您可以深入了解parse_cmd_line_arguments(). 此函数提供所有与 CLI 相关的功能:

 1# cli.py
 2# Snip...
 3
 4def parse_cmd_line_arguments():
 5    parser = argparse.ArgumentParser(
 6        prog="tree",
 7        description="RP Tree, a directory tree generator",
 8        epilog="Thanks for using RP Tree!",
 9    )
10    parser.version = f"RP Tree v{__version__}"
11    parser.add_argument("-v", "--version", action="version")
12    parser.add_argument(
13        "root_dir",
14        metavar="ROOT_DIR",
15        nargs="?",
16        default=".",
17        help="Generate a full directory tree starting at ROOT_DIR",
18    )
19    return parser.parse_args()

下面是这个函数的作用:

  • 第 5 行实例化argparse.ArgumentParser,提供应用程序的命令名称 ( prog)、description程序的简短内容以及epilog在用户运行应用程序的帮助选项后显示的短语。这个类为用户在命令行输入的所有参数提供了一个解析器。

  • 第 10行将解析器的version属性设置为包含应用程序名称及其当前版本的字符串__version__

  • 第 11行将第一个可选参数添加到应用程序的 CLI。该-v--version标志被要求提供这种说法,里面有你的终端窗口上显示应用程序的版本字符串的默认操作。

  • 第 12 到 18 行向CLI 添加了第二个参数。这里,root_dir是一个位置参数,它包含您将用作生成目录树图的起点的目录路径。在这种情况下,有四个参数.add_argument()

    1. metavar 在用法消息中保存参数的名称。

    2. nargs定义您的程序在手头的参数下可以采用的值的数量。例如,你的目录树生成器只能取一个在命令行目录路径,所以适当的值nargs"?"

    3. default为手头的参数提供默认值。在这种情况下,您使用点 ( ".") 将当前目录设置为默认根目录。

    4. help 提供描述参数作用的简短帮助消息。

  • 第 19 行使用 解析提供的参数.parse_args()。此方法返回一个Namespace包含所有提供的参数的对象。您可以使用命名空间对象上的点表示法访问这些参数。请注意,您在args编写main().

完成这一步旅程的最后一步是提供一个入口点脚本。回到您的代码编辑器并打开tree.py,然后向其中添加以下代码:

#!/usr/bin/env python3
# tree.py

"""This module provides RP Tree entry point script."""

from rptree.cli import main

if __name__ == "__main__":
    main()

该文件简短明了。您首先导入main()fromcli.py然后将其调用包装在传统if __name__ == "__main__":条件中,以便 Pythonmain()仅在您将文件作为程序运行而不是将其作为模块导入时才调用。

有了这个脚本,您就可以开始使用全新的命令行目录树生成器了。打开命令行窗口,移至项目目录,然后运行以下命令:

$ python tree.py ../hello
../hello/
│
├── hello/
│   ├── __init__.py
│   └── hello.py
│
├── tests/
│   └── test_hello.py
│
├── requirements.txt
├── setup.py
├── README.md
└── LICENSE

$ python tree.py -v
RP Tree v0.1.0

$ python tree.py --help
usage: tree [-h] [-v] [ROOT_DIR]

RP Tree, a directory tree generator

positional arguments:
  ROOT_DIR       Generate a full directory tree starting at ROOT_DIR

optional arguments:
  -h, --help     show this help message and exit
  -v, --version  show program's version number and exit

Thanks for using RP Tree!

就是这样!您的目录树生成器工具有效。它生成并在屏幕上显示用户友好的树形图。它还提供版本和使用信息。这对于大约一百行代码来说非常酷!在接下来的部分中,您将向应用程序添加更多功能。

步骤 4:实施仅目录选项

添加到目录树生成器的一个有趣功能是能够生成和显示仅目录树图。换句话说,一个只显示目录的图表。在此项目中,您将添加-d--dir-only标记来完成此操作,但在此之前,您需要进行更新_TreeGenerator以支持此新功能。

您可以通过单击以下链接下载将在此部分中添加或修改的文件和代码:

获取示例代码: 单击此处获取示例代码,您将在本教程中使用 Python 构建目录树生成器。

现在打开rptree.py模块并像这样更新它的代码:

# rptree.py
# Snip...

class _TreeGenerator:
    def __init__(self, root_dir, dir_only=False):
        self._root_dir = pathlib.Path(root_dir)
        self._dir_only = dir_only
        self._tree = []

    # Snip...

    def _tree_body(self, directory, prefix=""):
        entries = self._prepare_entries(directory)
        entries_count = len(entries)
        for index, entry in enumerate(entries):
            connector = ELBOW if index == entries_count - 1 else TEE
            if entry.is_dir():
                self._add_directory(
                    entry, index, entries_count, prefix, connector
                )
            else:
                self._add_file(entry, prefix, connector)

    def _prepare_entries(self, directory):
        entries = directory.iterdir()
        if self._dir_only:
            entries = [entry for entry in entries if entry.is_dir()]
            return entries
        entries = sorted(entries, key=lambda entry: entry.is_file())
        return entries

    # Snip...

首先,您将dir_only作为参数添加到类初始值设定项中。这是一个布尔参数,允许您根据用户在命令行的输入生成完整的树或仅目录树。此参数默认为False因为生成完整树是最常见的用例。

在第二个突出显示的行中,您创建一个实例属性,调用它._dir_only来保存新添加的参数。

在突出显示的第三行中,您将两行原始代码替换为对 的调用._prepare_entries()。顾名思义,此函数准备目录条目以生成完整树或仅目录树。

._prepare_entries(),你首先得到entries 发电机。该if语句检查是否._dir_onlyTrue。如果是这样,那么您使用列表理解过滤掉文件并返回list目录。如果._dir_onlyFalse,那么您对条目进行排序,重复使用您之前看到的相同代码。最后,您返回 中条目的完整列表directory

现在您需要确保将这个新参数传递给_TreeGeneratorback in的实例DirectoryTree

# rptree.py
# Snip...

class DirectoryTree:
    def __init__(self, root_dir, dir_only=False):
        self._generator = _TreeGenerator(root_dir, dir_only)

    # Snip...

在突出显示的第一行中,您添加一个新参数dir_only,该参数称为类初始值设定项。在突出显示的第二行中,确保将新参数传递给 的构造函数_TreeGenerator

完成这些更改后,您可以更新cli.py文件,以便应用程序可以在命令行中获取和处理-d--dir-only标志。首先,您需要更新main()

# cli.py
# Snip...

def main():
    # Snip...
    tree = DirectoryTree(root_dir, dir_only=args.dir_only)
    tree.generate()

在突出显示的行中,您传递args.dir_only给 的dir_only参数DirectoryTreeargs命名空间的这个属性包含一个取决于用户输入的布尔值。如果用户在命令行提供-dor--dir-only选项,则args.dir_onlyTrue。否则,它是False

接下来,将这些-d--dir-only标志添加到命令行界面。为此,您需要parse_cmd_line_arguments()像这样更新:

# cli.py
# Snip...

def parse_cmd_line_arguments():
    # Snip...
    parser.add_argument(
        "-d",
        "--dir-only",
        action="store_true",
        help="Generate a directory-only tree",
    )
    return parser.parse_args()

action调用中的参数.add_argument()保存 value "store_true",这意味着该参数自动存储TrueFalse根据用户的输入。在这种情况下,如果用户在命令行提供-dor--dir-only标志,则参数存储True. 否则,它存储False.

有了这个更新,就可以运行和测试应用程序了。返回终端窗口并执行以下命令:

$ python tree.py ../hello -d
../hello/
│
├── hello/
│
└── tests/

从现在开始,如果您在命令行中提供-d-dir-only标志,则树形图仅显示示例hello/目录中的子目录。

步骤 5:将目录树图保存到文件

在本节中,您将向目录树生成器工具添加最终功能。您将为应用程序提供将生成的目录树图保存到外部文件的功能。为此,您将使用标志-o--output-file.

像往常一样,要下载您将在此部分添加或修改的代码,请单击以下链接:

现在返回rptree.py并更新DirectoryTree如下:

# rptree.py
# Snip...
import sys

# Snip...

class DirectoryTree:
    def __init__(self, root_dir, dir_only=False, output_file=sys.stdout):
        self._output_file = output_file
        self._generator = _TreeGenerator(root_dir, dir_only)

    def generate(self):
        tree = self._generator.build_tree()
        if self._output_file != sys.stdout:
            # Wrap the tree in a markdown code block
            tree.insert(0, "```")
            tree.append("```")
            self._output_file = open(
                self._output_file, mode="w", encoding="UTF-8"
            )
        with self._output_file as stream:
            for entry in tree:
                print(entry, file=stream)

此更新几乎是对DirectoryTree. 首先,您向名为 的类初始值设定项添加一个新参数output_file。此参数默认为sys.stdout,这是标准输出(您的屏幕)。然后将新添加的参数存储在名为 的实例属性中._output_file

在 中.generate(),您首先构建目录树图并将其存储在tree. 条件语句检查用户是否提供了不同于sys.stdout. 如果是,则if代码块使用反引号 ( "```")将树形图包装在降价代码块中。

接下来,您使用打开提供的输出文件,open()以便您可以使用with语句处理它。

注意:.insert(0, x)就执行时间而言,调用列表对象可能是一项代价高昂的操作。这是因为 Python 需要将所有项目向右移动一个位置,然后在第一个位置插入新项目。

一种有效的替代方法.insert()collections.deque使用.appendleft(). 此数据结构针对此类操作进行了优化。

with块中,您开始for循环以将目录树图打印到提供的输出文件。请注意,print()也可以写入文件系统上的常规文件。为此,您只需要提供一个自定义file参数。要深入了解 的功能print(),请查看Python print() 函数指南

完成后DirectoryTree,您可以更新命令行界面以启用输出文件选项。返回cli.py并像这样修改它:

# cli.py
# Snip...

def main():
    # Snip...
    tree = DirectoryTree(
        root_dir, dir_only=args.dir_only, output_file=args.output_file
    )
    tree.generate()

def parse_cmd_line_arguments():
    # Snip...
    parser.add_argument(
        "-o",
        "--output-file",
        metavar="OUTPUT_FILE",
        nargs="?",
        default=sys.stdout,
        help="Generate a full directory tree and save it to a file",
    )
    return parser.parse_args()

第一步是将输出文件作为DirectoryTree构造函数中的参数。输出文件(如果有)将存储在args.output_file.

接下来,您将一个新参数添加到parser. 这个参数有两个标志:-o--output-file。要提供替代输出文件,用户必须使用这些标志之一并在命令行中提供文件的路径。请注意,输出文件默认为sys.stdout. 这样,如果用户不提供输出文件,则应用程序会自动使用标准输出屏幕。

您可以通过在终端上运行以下命令来测试新添加的选项:

$ python tree.py ../hello -o output_file.md

此命令生成一个完整的目录树图并将其保存到output_file.md当前目录中的文件中。如果您打开该文件,那么您将看到以降价格式保存在那里的目录树图。

就是这样!您的目录树生成器项目已完成。除了生成和显示完整目录树图的默认选项外,该应用程序还提供以下选项:

  • -v,--version显示当前版本信息并退出应用程序。
  • -h,--help显示帮助和使用信息。
  • -d--dir-only生成仅目录树并将其打印到屏幕上。
  • -o--output-to-markdown生成一棵树,并以markdown格式保存到文件中。

您现在拥有一个功能齐全的命令行工具,可以生成用户友好的目录树图。很好!

结论

您可以通过创建CLI工具和应用程序来自动化和加速工作环境中的多个流程和任务。在 Python 中,您可以使用argparse或其他第三方库快速创建此类工具。在本教程中,您编写了一个完整的项目来为命令行构建 Python目录树生成器工具。

该应用程序在命令行中获取目录路径,生成目录树图,并将其显示在终端窗口中或将其保存到文件系统上的外部文件中。它还提供了更多选项来调整生成的树图。

在本教程中,您学习了如何:

  • 使用 Python 的创建CLI 应用程序argparse
  • 递归遍历目录结构使用pathlib
  • 生成、格式化和打印目录树图
  • 将目录树图保存到输出文件

目录树生成器项目的最终源代码可供您下载。要获取它,请单击以下链接:

下一步

到目前为止,您已经构建了一个功能齐全的目录树生成器工具。尽管该应用程序提供了最少的功能集,但它是您继续添加功能并在此过程中学习的良好起点。这将帮助您将 Python 和 CLI 应用程序的技能提升到一个新的水平。

以下是您可以实施以继续改进目录树生成器工具的一些想法:

  • 添加对文件和目录排序的支持:对文件和目录进行排序的能力是一项很棒的功能。例如,您可以添加-s--sort-tree布尔标志,以允许用户调整在最后的树形图的文件和目录的顺序。

  • 向树状图添​​加图标和颜色:添加图标、字体颜色或两者都是实现的一个很好的功能。例如,您可以为目录使用自定义文件夹图标,为文件使用基于文件类型的图标。

  • 设置应用程序以将其作为开源项目发布准备将应用程序作为开源项目发布到 PyPI 可能是一个有趣的挑战。这样做可以让您与朋友和同事分享您的工作。要开始将包发布到 PyPI,请查看如何将开源 Python 包发布到 PyPI

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。