📜  Python中的命令行界面编程

📅  最后修改于: 2022-05-13 01:55:18.146000             🧑  作者: Mango

Python中的命令行界面编程

本文讨论如何使用我们制作基本“文本文件管理器”的示例为您的Python程序创建 CLI。
让我们先讨论一些基础知识。

什么是命令行界面 (CLI)?

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

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

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

  • 即使您的程序只有一个非常基本的命令行界面 (CLI),也可以让每个人的修改参数的工作变得更轻松,包括程序员,也包括非程序员。
  • 程序的 CLI 还可以更轻松地在程序中自动运行和修改变量,当您想使用 cronjob 或 os.system 调用运行程序时。

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

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

好的,让我们从一个非常基本的程序开始,以了解 argparse 的作用。

Python
# importing required modules
import argparse
 
# create a parser object
parser = argparse.ArgumentParser(description = "An addition program")
 
# add argument
parser.add_argument("add", nargs = '*', metavar = "num", type = int,
                     help = "All the numbers separated by spaces will be added.")
 
# parse the arguments from standard input
args = parser.parse_args()
 
# check if add argument has any input data.
# If it has, then print sum of the given numbers
if len(args.add) != 0:
    print(sum(args.add))


Python
# importing the required modules
import os
import argparse
 
# error messages
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()
    else if not valid_filetype(file_name):
        print(INVALID_FILETYPE_MSG%(file_name))
        quit()
    return
     
def valid_filetype(file_name):
    # validate file type
    return file_name.endswith('.txt')
 
def valid_path(path):
    # validate file path
    return os.path.exists(path)
         
     
 
def read(args):
    # get the file name/path
    file_name = args.read[0]
 
    # validate the file name/path
    validate_file(file_name)
 
    # read and print the file content
    with open(file_name, 'r') as f:
        print(f.read())
 
 
def show(args):
    # get path to directory
    dir_path = args.show[0]
     
    # validate path
    if not valid_path(dir_path):
        print("Error: No such directory found.")
        exit()
 
    # get text files in directory
    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):
    # get the file name/path
    file_name = args.delete[0]
 
    # validate the file name/path
    validate_file(file_name)
     
    # delete the file
    os.remove(file_name)
    print("Successfully deleted {}.".format(file_name))
     
 
def copy(args):
    # file to be copied
    file1 = args.copy[0]
    # file to copy upon
    file2 = args.copy[1]
 
    # validate the file to be copied
    validate_file(file1)
 
    # validate the type of file 2
    if not valid_filetype(file2):
        print(INVALID_FILETYPE_MSG%(file2))
        exit()
 
    # copy file1 to 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 file name
    old_filename = args.rename[0]
    # new file name
    new_filename = args.rename[1]
 
    # validate the file to be renamed
    validate_file(old_filename)
 
    # validate the type of new file name
    if not valid_filetype(new_filename):
        print(INVALID_FILETYPE_MSG%(new_filename))
        exit()
 
    # renaming
    os.rename(old_filename, new_filename)
    print("Successfully renamed {} to {}.".format(old_filename, new_filename))
def main():
    # create parser object
    parser = argparse.ArgumentParser(description = "A text file manager!")
 
    # defining arguments for parser object
    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.")
 
    # parse the arguments from standard input
    args = parser.parse_args()
     
    # calling functions depending on type of argument
    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__":
    # calling the main function
    main()


让我们看一下与上述程序相关的一些要点:

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

2

  • 现在,让我们看一下调用位置参数add的另一个示例。

  • 另一个值得一提的特殊功能是,当用户给程序提供无效参数时,argparse 如何发出错误。

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

Python

# importing the required modules
import os
import argparse
 
# error messages
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()
    else if not valid_filetype(file_name):
        print(INVALID_FILETYPE_MSG%(file_name))
        quit()
    return
     
def valid_filetype(file_name):
    # validate file type
    return file_name.endswith('.txt')
 
def valid_path(path):
    # validate file path
    return os.path.exists(path)
         
     
 
def read(args):
    # get the file name/path
    file_name = args.read[0]
 
    # validate the file name/path
    validate_file(file_name)
 
    # read and print the file content
    with open(file_name, 'r') as f:
        print(f.read())
 
 
def show(args):
    # get path to directory
    dir_path = args.show[0]
     
    # validate path
    if not valid_path(dir_path):
        print("Error: No such directory found.")
        exit()
 
    # get text files in directory
    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):
    # get the file name/path
    file_name = args.delete[0]
 
    # validate the file name/path
    validate_file(file_name)
     
    # delete the file
    os.remove(file_name)
    print("Successfully deleted {}.".format(file_name))
     
 
def copy(args):
    # file to be copied
    file1 = args.copy[0]
    # file to copy upon
    file2 = args.copy[1]
 
    # validate the file to be copied
    validate_file(file1)
 
    # validate the type of file 2
    if not valid_filetype(file2):
        print(INVALID_FILETYPE_MSG%(file2))
        exit()
 
    # copy file1 to 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 file name
    old_filename = args.rename[0]
    # new file name
    new_filename = args.rename[1]
 
    # validate the file to be renamed
    validate_file(old_filename)
 
    # validate the type of new file name
    if not valid_filetype(new_filename):
        print(INVALID_FILETYPE_MSG%(new_filename))
        exit()
 
    # renaming
    os.rename(old_filename, new_filename)
    print("Successfully renamed {} to {}.".format(old_filename, new_filename))
def main():
    # create parser object
    parser = argparse.ArgumentParser(description = "A text file manager!")
 
    # defining arguments for parser object
    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.")
 
    # parse the arguments from standard input
    args = parser.parse_args()
     
    # calling functions depending on type of argument
    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__":
    # calling the main function
    main()

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

  • 帮助信息( Python脚本已保存为tfmanager.py):

  • 以下是使用文本文件管理器的操作示例: