📜  Python命令行界面编程

📅  最后修改于: 2020-05-05 11:54:14             🧑  作者: Mango

本文讨论一个示例,其中我们创建了一个基本的“文本文件管理器”,从而可以为Python程序创建CLI。
让我们首先讨论一些基础知识。

什么是命令行界面(CLI)?
命令行界面或命令语言解释器(CLI),也称为命令行用户界面,控制台用户界面和字符用户界面(CUI),是与计算机程序进行交互的一种方式,其中用户(或客户端)以连续的文本行(命令行)的形式向程序发出命令。(Wiki)
CLI的优点:

  • 需要更少的资源
  • 简洁而强大
  • 专家友好
  • 通过脚本更易于自动化

为什么要在Python程序中使用CLI?

  • 甚至为您的程序提供一个非常基本的命令行界面(CLI),都可以使每个人的生活变得更轻松,包括程序员,以及非程序员,都可以轻松地修改参数。
  • 程序的CLI还可简化在程序中自动运行和修改变量的过程,例如,当您想通过cronjob或os.system调用来运行程序时。

 

现在,让我们开始制作“文本文件管理器”。在这里,我们将使用一个名为Argparse的内置Python库。
关于Argparse:

  • 它使编写用户友好的命令行界面变得容易。
  • 该程序定义了所需的参数,而argparse将找出如何从sys.argv中解析这些参数。
  • argparse模块还会自动生成帮助和使用消息,并在用户为程序提供无效参数时发出错误。

好吧,让我们从一个真正的基本程序开始,以了解argparse的功能。

# 导入所需的模块
import argparse
# 创建一个解析器对象
parser = argparse.ArgumentParser(description = "An addition program")
# 添加参数
parser.add_argument("add", nargs = '*', metavar = "num", type = int,
                     help = "All the numbers separated by spaces will be added.")
# 解析来自标准输入的参数
args = parser.parse_args()
# 检查add参数是否有任何输入数据。 如果有,则打印给定数字的总和
if len(args.add) != 0:
    print(sum(args.add))

让我们审视与上述程序有关的一些重要点:

  • 首先,我们导入了argparse模块。
  • 然后,创建了一个ArgumentParser对象,并提供了我们程序的描述。
  • 现在,我们可以通过添加参数来为解析器对象填充信息。在此示例中,我们创建了一个参数add。 许多参数可以传递给add_argument函数。在这里,我将解释上面示例中使用的参数
    参数1 :( “ add”)是参数的名称。我们将使用此名称通过键入args.add访问
    add参数。
    参数2:(nargs =’*’)应该使用的命令行参数的数量。将其指定为“ *”表示可以为任何参数,即从0到任何值。
    参数3:(metavar =’num’)使用情况中参数的名称。
    参数4:
    type = int)命令行参数应转换为的类型,默认为str。
    参数5 :(help)对参数的简要说明。
  • 一旦指定了所有参数,就可以从标准命令行输入流中解析参数了。为此,我们使用parse_args()函数。
  • 现在,您可以简单地检查输入是否调用了特定的参数。在这里,我们检查args.add的长度,以检查是否从输入接收到任何数据。注意,参数的值是作为列表获得的
  • 有两种类型的参数:位置参数和可选参数。
    位置不需要调用任何规范的位置。而可选参数必须首先通过其名称指定(以“ –”符号开头,“-“也是简写形式)。

  • 始终可以使用–help或-h可选参数来查看帮助消息。
    这是一个示例(Python脚本已保存为add.py):
  • 现在,让我们看一下另一个示例,其中调用了位置参数add
  • 值得一提的另一个特殊功能是当用户为程序提供无效参数时argparse如何发出错误。

因此,这是一个基本示例,因此您可以熟悉argparse和CLI概念。现在,让我们进入“文本文件管理器”程序。

# 导入所需的模块
import os
import argparse
# 错误讯息
INVALID_FILETYPE_MSG = "Error: Invalid file format. %s must be a .txt file."
INVALID_PATH_MSG = "Error: Invalid file path/name. Path %s does not exist."
def validate_file(file_name):
    '''
    validate file name and path.
    '''
    if not valid_path(file_name):
        print(INVALID_PATH_MSG%(file_name))
        quit()
    elif not valid_filetype(file_name):
        print(INVALID_FILETYPE_MSG%(file_name))
        quit()
    return
def valid_filetype(file_name):
    # 验证文件类型
    return file_name.endswith('.txt')
def valid_path(path):
    # 验证文件路径
    return os.path.exists(path)
def read(args):
    # 获取文件名/路径
    file_name = args.read[0]
    # validate the file name/path
    validate_file(file_name)
    # 读取并打印文件内容
    with open(file_name, 'r') as f:
        print(f.read())
def show(args):
    # 获取目录路径
    dir_path = args.show[0]
    # 验证路径
    if not valid_path(dir_path):
        print("Error: No such directory found.")
        exit()
    # 获取目录中的文本文件
    files = [f for f in os.listdir(dir_path) if valid_filetype(f)]
    print("{} text files found.".format(len(files)))
    print('\n'.join(f for f in files))
def delete(args):
    # 获取文件名/路径
    file_name = args.delete[0]
    # 验证文件名/路径
    validate_file(file_name)
    # 删除文件
    os.remove(file_name)
    print("Successfully deleted {}.".format(file_name))
def copy(args):
    # 要复制的文件
    file1 = args.copy[0]
    # 要复制的文件
    file2 = args.copy[1]
    # 验证要复制的文件
    validate_file(file1)
    # 验证文件类型2
    if not valid_filetype(file2):
        print(INVALID_FILETYPE_MSG%(file2))
        exit()
    # 将file1复制到file2
    with open(file1, 'r') as f1:
        with open(file2, 'w') as f2:
            f2.write(f1.read())
    print("Successfully copied {} to {}.".format(file1, file2))
def rename(args):
    # 旧文件名
    old_filename = args.rename[0]
    # 新文件名
    new_filename = args.rename[1]
    # 验证要重命名的文件
    validate_file(old_filename)
    # 验证新文件名的类型
    if not valid_filetype(new_filename):
        print(INVALID_FILETYPE_MSG%(new_filename))
        exit()
    # 重命名
    os.rename(old_filename, new_filename)
    print("Successfully renamed {} to {}.".format(old_filename, new_filename))
def main():
    # 创建解析器对象
    parser = argparse.ArgumentParser(description = "A text file manager!")
    # 为解析器对象定义参数
    parser.add_argument("-r", "--read", type = str, nargs = 1,
                        metavar = "file_name", default = None,
                        help = "Opens and reads the specified text file.")
    parser.add_argument("-s", "--show", type = str, nargs = 1,
                        metavar = "path", default = None,
                        help = "Shows all the text files on specified directory path.\
                        Type '.' for current directory.")
    parser.add_argument("-d", "--delete", type = str, nargs = 1,
                        metavar = "file_name", default = None,
                        help = "Deletes the specified text file.")
    parser.add_argument("-c", "--copy", type = str, nargs = 2,
                        metavar = ('file1','file2'), help = "Copy file1 contents to \
                        file2 Warning: file2 will get overwritten.")
    parser.add_argument("--rename", type = str, nargs = 2,
                        metavar = ('old_name','new_name'),
                        help = "Renames the specified file to a new name.")
    # 解析来自标准输入的参数
    args = parser.parse_args()
    # 根据参数类型调用函数
    if args.read != None:
        read(args)
    elif args.show != None:
        show(args)
    elif args.delete !=None:
        delete(args)
    elif args.copy != None:
        copy(args)
    elif args.rename != None:
        rename(args)
if __name__ == "__main__":
    # 调用main函数
    main()

在前面的示例之后,以上代码似乎很容易解释。
我们所做的就是为文件管理器程序添加一组参数。请注意,所有这些参数都是可选参数。因此,我们使用一些if-elif语句来将命令行输入与正确的参数类型函数进行匹配,以便可以处理查询。
这是描述上述程序用法的一些屏幕截图:

  • 帮助消息(Python脚本已保存为tfmanager.py):
  • 以下是使用文本文件管理器进行操作的示例:

因此,这是我们制作的一个简单的CLI Python程序的示例。Argparse模块可以轻松创建许多复杂的CLI。我希望这些例子能使您在这一领域起步。