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):
- 现在,让我们看一下调用位置参数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):
- 以下是使用文本文件管理器的操作示例: