利用Python内置库实现创建命令行应用程序
本文深入讲解如何利用Python内置的argparse库开发命令行应用程序。内容涵盖从基础的sys.argv对比,到使用argparse解析参数、生成帮助信息、设置参数别名及互斥组。通过构建文件搜索工具的实际案例,展示如何处理路径、扩展名及文件大小过滤,帮助开发者掌握构建灵活CLI工具的核心技能。
介绍
在开发应用程序时,通常需要为用户提供操作指引。常见的交互方式有两种:一是通过命令行参数传递指令,二是构建图形用户界面(GUI)。部分应用程序会同时支持这两种交互模式。
在服务器环境中运行代码时,命令行接口尤为关键。大多数服务器,尤其是Linux系统,默认不安装图形界面。即便尝试运行GUI应用,也可能因环境限制而无法实现。
Python标准库提供了argparse模块,用于简化命令行界面的创建。本文将介绍该模块的核心用法。
- 解析实参
- 创建有用的信息
- 添加别名
- 使用相互排斥的参数
- 创建一个简单的搜索工具
argparse模块的功能远超本文所述范围。如需深入了解,建议查阅官方文档。
接下来,我们将开始解析命令行参数。
解析参数
在掌握argparse之前,了解另一种传递参数的方法很有必要。Python脚本允许接收任意参数,并通过sys模块进行访问。
为演示其工作原理,创建一个名为sys_args.py的文件,并输入以下代码:
# sys_args.py
import sys
def main():
print('You passed the following arguments:')
print(sys.argv)
if __name__ == '__main__':
main()该代码导入sys模块并打印sys.argv的内容。argv属性是一个列表,包含传递给脚本的所有参数,其中第一个元素为脚本名称本身。
以下示例展示了运行代码并传入多个参数时的输出结果。
$ python3 sys_args.py --s 45
You passed the following arguments:
['sys_args.py', '--s', '45']使用sys.argv的主要缺陷在于无法对传入参数进行控制。
- 无法忽略特定参数
- 无法定义默认参数值
- 无法验证参数的有效性
因此,使用argparse是调用Python标准库的最佳实践。该模块功能强大且实用。我们首先回顾命令行应用程序的常见处理流程。
- pass:传入一个文件
- do:在程序中对该文件进行处理
- output:输出处理结果
以下是一个通用实现示例。请创建file_parser.py并添加如下代码。
# file_parser.py
import argparse
def file_parser(input_file, output_file=''):
print(f'Processing {input_file}')
print('Finished processing')
if output_file:
print(f'Creating {output_file}')
def main():
parser = argparse.ArgumentParser('File parser')
parser.add_argument('--infile', help='Input file')
parser.add_argument('--out', help='Output file')
args = parser.parse_args()
if args.infile:
file_parser(args.infile, args.out)
if __name__ == '__main__':
main()file_parser()函数负责执行解析逻辑。此例中,它仅接收一个文件名并打印输出。output_file参数的默认值为空字符串。
程序的核心逻辑位于main()中。此处实例化argparse.ArgumentParser()并为其命名。随后添加两个参数:--infile和--out。为使用解析器,需调用parse_args(),该方法返回传递的有效参数。最后,检查用户是否使用了--infile标志。若已使用,则调用file_parser()函数。
以下是在终端中运行代码的方法。
$ python file_parser.py --infile something.txt
Processing something.txt
Finished processing此处,使用--infile标志及文件名运行脚本。这将触发main()函数,进而调用file_parser()。
下一步,尝试使用代码中声明的两个命令行参数运行应用程序。
$ python file_parser.py --infile something.txt --out output.txt
Processing something.txt
Finished processing
Creating output.txt此次输出增加了一行,提及输出文件名。这代表代码逻辑中的一个分支。当指定输出文件时,可通过新代码块或函数生成该文件。若未指定输出文件,则相关代码块不会执行。
使用argparse创建命令行工具时,可轻松添加提示信息,帮助用户在不确定如何正确交互时获得指引。
接下来,我们将探讨如何从应用程序中获取帮助信息。
创建帮助信息
argparse库会利用创建每个参数时提供的信息,自动为应用程序生成有用的帮助文档。代码如下:
# file_parser.py
import argparse
def file_parser(input_file, output_file=''):
print(f'Processing {input_file}')
print('Finished processing')
if output_file:
print(f'Creating {output_file}')
def main():
parser = argparse.ArgumentParser('File parser')
parser.add_argument('--infile', help='Input file')
parser.add_argument('--out', help='Output file')
args = parser.parse_args()
if args.infile:
file_parser(args.infile, args.out)
if __name__ == '__main__':
main()现在尝试使用-h标志运行代码,应看到以下输出。
$ file_parser.py -h
usage: File parser [-h] [--infile INFILE] [--out OUT]
optional arguments:
-h, --help show this help message and exit
--infile INFILE Input file
--out OUT Output fileadd_argument()的帮助参数用于生成上述帮助信息。argparse会自动添加-h和-help选项。通过提供描述和后记,可使帮助信息更加丰富。
让我们利用这些功能改进帮助信息。首先,将上述代码复制到一个名为file_parser_with_description.py的新文件中,并修改如下。
# file_parser_with_description.py
import argparse
def file_parser(input_file, output_file=''):
print(f'Processing {input_file}')
print('Finished processing')
if output_file:
print(f'Creating {output_file}')
def main():
parser = argparse.ArgumentParser(
'File parser',
description='PyParse - The File Processor',
epilog='Thank you for choosing PyParse!',
)
parser.add_argument('--infile', help='Input file for conversion')
parser.add_argument('--out', help='Converted output file')
args = parser.parse_args()
if args.infile:
file_parser(args.infile, args.out)
if __name__ == '__main__':
main()此处,将description和epilog参数传递给ArgumentParser。同时更新了add_argument()的帮助参数,使其更具描述性。
进行上述修改后,使用-h或--help运行脚本,将看到以下输出。
$ python file_parser_with_description.py -h
usage: File parser [-h] [--infile INFILE] [--out OUT]
PyParse - The File Processor
optional arguments:
-h, --help show this help message and exit
--infile INFILE Input file for conversion
--out OUT Converted output file
Thank you for choosing PyParse!现在,帮助输出中包含了新的description和epilog内容。这为命令行程序增添了一些额外细节。
此外,可通过ArgumentParser的add_help参数完全禁用应用程序中的帮助功能。若认为帮助文本过于冗长,可按以下方式禁用它。
# file_parser_no_help.py
import argparse
def file_parser(input_file, output_file=''):
print(f'Processing {input_file}')
print('Finished processing')
if output_file:
print(f'Creating {output_file}')
def main():
parser = argparse.ArgumentParser(
'File parser',
description='PyParse - The File Processor',
epilog='Thank you for choosing PyParse!',
add_help=False,
)
parser.add_argument('--infile', help='Input file for conversion')
parser.add_argument('--out', help='Converted output file')
args = parser.parse_args()
if args.infile:
file_parser(args.infile, args.out)
if __name__ == '__main__':
main()将add_help设置为False,即可禁用-h和--help标志。
以下演示展示了这一效果。
$ python file_parser_no_help.py --help
usage: File parser [--infile INFILE] [--out OUT]
File parser: error: unrecognized arguments: --help在下一节中,你将学习如何为参数添加别名!
添加别名
别名是指使用替代标志执行相同操作。例如,你可知晓使用-h和--help均可访问程序帮助信息。-h是--help的别名,反之亦然。
查看main()中parser.add_argument()方法的变化。
# file_parser_aliases.py
import argparse
def file_parser(input_file, output_file=''):
print(f'Processing {input_file}')
print('Finished processing')
if output_file:
print(f'Creating {output_file}')
def main():
parser = argparse.ArgumentParser(
'File parser',
description='PyParse - The File Processor',
epilog='Thank you for choosing PyParse!',
add_help=False,
)
parser.add_argument('-i', '--infile', help='Input file for conversion')
parser.add_argument('-o', '--out', help='Converted output file')
args = parser.parse_args()
if args.infile:
file_parser(args.infile, args.out)
if __name__ == '__main__':
main()此处修改了第一个add_argument(),除了接受--infile外,还接受了-i。同时在第二个add_argument()中加入了-o。这样即可使用两个新的快捷标志运行代码。
以下是一个示例。
$ python3 file_parser_aliases.py -i something.txt -o output.txt
Processing something.txt
Finished processing
Creating output.txt查阅argparse文档可知,子解析器也可添加别名。子解析器用于在应用程序中创建子命令,以执行其他操作。一个典型例子是Docker,一款虚拟化或容器应用。它提供一系列命令,可在docker下运行,如docker compose等。这些命令中的每一个都有独立的子命令可供使用。
以下是一个典型的docker命令,用于运行容器。
docker exec -it container_name bash这将启动一个名为container_name的容器。若使用docker compose,则将使用一组不同的命令。exec和compose即为subparsers的例子。
使用相互排斥的参数
有时需让应用程序接受某些参数,但不接受其他参数。例如,可能希望限制应用程序仅能创建或删除文件,而不能同时执行这两项操作。
argparse模块提供了add_mutually_exclusive_group()方法,可实现此功能。
将两个参数添加到一个组对象中,使其相互排斥,如下例所示。
# file_parser_exclusive.py
import argparse
def file_parser(input_file, output_file=''):
print(f'Processing {input_file}')
print('Finished processing')
if output_file:
print(f'Creating {output_file}')
def main():
parser = argparse.ArgumentParser(
'File parser',
description='PyParse - The File Processor',
epilog='Thank you for choosing PyParse!',
add_help=False,
)
group = parser.add_mutually_exclusive_group()
group.add_argument('-i', '--infile', help='Input file for conversion')
group.add_argument('-o', '--out', help='Converted output file')
args = parser.parse_args()
if args.infile:
file_parser(args.infile, args.out)
if __name__ == '__main__':
main()首先,创建一个互斥组。随后,将-i和-o参数添加到该组中,而非解析器对象。现在这两个参数即为互斥关系。
以下展示了尝试使用这两个参数运行代码时的结果。
$ python3 file_parser_exclusive.py -i something.txt -o output.txt
usage: File parser [-i INFILE | -o OUT]
File parser: error: argument -o/--out: not allowed with argument -i/--infile同时使用这两个参数运行代码,解析器将向用户显示错误信息,解释其操作错误之处。
在涵盖所有与使用argparse相关的信息后,你已准备好应用新技能来创建一个简单的搜索工具。
创建一个简单的搜索工具
在开始创建应用程序之前,明确任务目标总是有益的。本节中希望建立的应用程序应能搜索特定文件类型的文件。为增加趣味性,可添加一个额外参数,允许选择性搜索特定大小的文件。
可使用Python的glob模块来搜索文件类型。可在此处阅读关于该模块的详细信息。
还有一个fnmatch模块,glob自身也使用它。目前建议使用glob,因其更易于使用。但若有兴趣编写更专业的代码,fnmatch可能是更佳选择。
然而,由于需通过文件大小选择性过滤返回的文件,可使用pathlib,它包含类似glob的接口。glob模块本身不提供文件大小信息。
可先创建一个名为pysearch.py的文件并输入以下代码。
# pysearch.py
import argparse
import pathlib
def search_folder(path, extension, file_size=None):
"""
Search folder for files
"""
folder = pathlib.Path(path)
files = list(folder.rglob(f'*.{extension}'))
if not files:
print(f'No files found with {extension=}')
return
if file_size is not None:
files = [
f
for f in files
if f.stat().st_size >= file_size
]
print(f'{len(files)} *.{extension} files found:')
for file_path in files:
print(file_path)在上述代码片段中,首先导入了argparse和pathlib。接下来,创建了search_folder()函数,它接收三个参数。
- path - 要搜索的文件夹
- extension - 要寻找的文件扩展名
- file_size - 要过滤的文件大小,以字节为单位。
将路径转换为pathlib.Path对象,然后使用其rglob()方法在文件夹中搜索用户传入的扩展名。若未找到文件,则向用户打印一条有意义的信息并退出。
若找到任何文件,则检查是否已设置filesize。若已设置,则使用列表推导式过滤出小于指定filesize的文件。
接下来,打印找到的文件数量,最后在这些文件上循环,打印其名称。
为使这一切正常运行,需创建一个命令行界面。可通过添加包含argparse代码的main()函数来实现,如下所示。
def main():
parser = argparse.ArgumentParser(
'PySearch',
description='PySearch - The Python Powered File Searcher',
)
parser.add_argument('-p', '--path',
help='The path to search for files',
required=True,
dest='path')
parser.add_argument('-e', '--ext',
help='The extension to search for',
required=True,
dest='extension')
parser.add_argument('-s', '--size',
help='The file size to filter on in bytes',
type=int,
dest='size',
default=None)
args = parser.parse_args()
search_folder(args.path, args.extension, args.size)
if __name__ == '__main__':
main()该ArgumentParser()包含三个参数,与传递给search_folder()的参数相对应。使--path和--ext参数成为必需的,而--size参数成为可选的。注意,--size参数被设置为type=int,这意味着不能将其传为字符串。
add_argument()函数有一个新参数。它是dest参数,可用于告知参数分析器将传递的参数保存在何处。
以下是一个脚本运行的示例。
$ python3 pysearch.py -p /Users/michael/Dropbox/python101code/chapter32_argparse -e py -s 650
6 *.py files found:
/Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_aliases2.py
/Users/michael/Dropbox/python101code/chapter32_argparse/pysearch.py
/Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_aliases.py
/Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_with_description.py
/Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_exclusive.py
/Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_no_help.py现在尝试使用-s及一个字符串运行它。
$ python3 pysearch.py -p /Users/michael/Dropbox/python101code/chapter32_argparse -e py -s python
usage: PySearch [-h] -p PATH -e EXTENSION [-s SIZE]
PySearch: error: argument -s/--size: invalid int value: 'python'此次收到错误信息,因为-s和-size仅接受整数。在你自己的机器上运行此段代码,查看当使用-s及整数时,它是否按预期工作。
这里有一些想法,可用于改进你的代码版本。
更好地处理扩展文件。目前,它将接受*.py,这不会如预期那样工作。
更新代码,以便可一次搜索多个扩展名
更新代码,以便对文件大小范围进行过滤(例如,1MB-5MB)。
还有许多其他功能和改进可添加到此代码中,例如添加错误处理或单元测试。
总结
argparse模块功能齐全,可用于创建庞大且灵活的命令行应用程序。在本章中,你了解了以下内容。
- 解析参数
- 创建有用的信息
- 添加别名
- 使用相互排斥的参数
- 创建一个简单的搜索工具
你可以用argparse模块做更多事情,不完全包括本章所讲的。请务必查看文档以了解全部细节。
到此这篇关于利用Python内置库实现创建命令行应用程序的文章就介绍到这了,更多相关Python命令行应用程序内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













