详解Python如何优雅地解析命令行
作者:编程学习网 发布时间:2021-02-16 01:53:59
如何优雅地解析命令行选项
随着我们编程经验的增长,对命令行的熟悉程度日渐加深,想来很多人会渐渐地体会到使用命令行带来的高效率。
自然而然地,我们自己写的很多程序(或者干脆就是脚本),也希望能够像原生命令和其他程序一样,通过运行时输入的参数就可以设定、改变程序的行为;而不必一层层找到相应的配置文件,然后还要定位到相应内容、修改、保存、退出……
想想就很麻烦好吗
1. 手动解析
所以让我们开始解析命令行参数吧~
在以前关于模块的文章中我们提到过sys.args这个变量,其中保存的就是调用当前脚本时传入的命令行参数。
我们先观察一下这个变量:
# test_sys.py
import sys
print(sys.argv)
通过命令行调用:
$ python test_sys.py -d today -t now --author justdopython --country China --auto
得到如下输出结果:
['test_sys.py', '-d', 'today', '-t', 'now', '--author', 'justdopython', '--country', 'China', '--auto']
可见,sys.argv其实就是将命令行参数按空格切分,得到的一个字符串列表。此外,命令行参数的第一个就是当前运行的脚本名称。
我们如果想要提取出各个参数及其对应的值,首先得区分出命令行的长参数和短参数,它们分别由“--”和“-”开头作为标识。所以我们也以此作为判断长短参数的条件:
import sys
for command_arg in sys.argv[1:]:
if command_arg.startswith('--'):
print("%s 为长参数" % command_arg)
elif command_arg.startswith('-'):
print("%s 为短参数" % command_arg)
测试结果:
$ python manually_parse_argv.py -d today -t now --author justdopython --country China --auto
-d 为短参数
-t 为短参数
--author 为长参数
--country 为长参数
--auto 为长参数
紧接着,我们需要在解析出长短参数这一步的基础上,再解析出对应的参数值:
# manually_parse_argv.py
import sys
# 由于sys.argv的第一个变量是当前脚本名称,因此略过
for index, command_arg in enumerate(sys.argv[1:]):
if command_arg.startswith('--'):
try:
value = sys.argv[1:][index+1]
if not value.startswith('-'):
print("%s 为长参数,参数值为 %s" % (command_arg, value))
continue
except IndexError:
pass
print("%s 为长参数,无参数值" % command_arg)
elif command_arg.startswith('-'):
try:
value = sys.argv[1:][index+1]
if not value.startswith('-'):
print("%s 为短参数,参数值为 %s" % (command_arg, value))
continue
except IndexError:
pass
print("%s 为短参数,无参数值" % command_arg)
再测试一下:
$ python manually_parse_argv.py -d today -t now --author justdopython --country China --auto
-d 为短参数,参数值为 today
-t 为短参数,参数值为 now
--author 为长参数,参数值为 justdopython
--country 为长参数,参数值为 China
--auto 为长参数,无参数值
看起来还不错。
但是再看看我们的代码……真正的逻辑还没开始,反倒是为了解析命令行参数已经写了几十行代码。这一点都不pythonic——这还不包括一些其他关于异常情况的处理。
更何况是要在每个类似的程序中加入这么一段程序了。
2. getopt模块
Python的好处就在于,生态过于丰富,几乎你要用到的每个功能,都已经有人为你写好了现成的模块以供调用。
衣来伸手饭来张口的日子除了能在梦中想想,在用Python写程序的时候也不是不可以奢望。
比如命令行参数解析,就有一个名为getopt的模块,既能够准确区分长短命令行参数,也能够恰当地提取命令行参数的值。
咱们先来看看:
# test_getopt.py
import sys
import getopt
opts, args = getopt.getopt(sys.argv[1:], 'd:t:', ["author=", "country=", "auto"])
print(opts)
print(args)
打印结果:
$ python test_getopt.py -d today -t now --author justdopython --country China --auto
[('-d', 'today'), ('-t', 'now'), ('--author', 'justdopython'), ('--country', 'China'), ('--auto', '')]
[]
下面我们来分别解释一下相关参数的含义。
getopt模块中的getopt函数用于解析命令行参数。
该函数接受三个参数:args,shortopts和longopts,分别代表“命令行参数”,“要接收的短选项”和“要接收的长选项”。
其中args和longopts均为字符串组成的列表,而shortopts则为一个字符串。
同样地,由于sys.argv的第一个值为当前脚本名称,所以多数情况下我们会选择向args参数传入sys.argv[1:]的值。
而shortopts这个参数接受的字符串则表示需要解析哪些短选项,字符串中每个字母均表示一个短选项:
import sys
import getopt
opts, args = getopt.getopt(sys.argv[1:], 'dt')
print(opts)
print(args)
输出结果:
$ python test_getopt.py -d -t
[('-d', ''), ('-t', '')]
[]
当然,如果输入的参数少于预期,也不会导致解析失败:
$ python test_getopt.py -t
[('-t', '')]
[]
但要是给出了预期之外的参数,就会导致模块抛错:
$ python test_getopt.py -d -t -k
Traceback (most recent call last):
File "test_getopt.py", line 11, in <module>
opts, args = getopt.getopt(sys.argv[1:], 'dt')
...
raise GetoptError(_('option -%s not recognized') % opt, opt)
getopt.GetoptError: option -k not recognized
这样的处理逻辑也符合我们使用命令的体验,可以简单地理解为“宁缺毋滥”。
如果短参数相应的字母后带了一个冒号:,则意味着这个参数需要指定一个参数值。getopt会将该参数对应的下一个命令行参数作为参数值(而不论下一个参数是什么形式):
import sys
import getopt
opts, args = getopt.getopt(sys.argv[1:], 'd:t')
print(opts)
print(args)
# $ python test_getopt.py -d -t
# [('-d', '-t')]
# []
此外,一旦getopt在预期接收到长短选项的位置没有找到以“--”或“-”开头的字符串,就会终止解析过程,剩下的未解析字符串均放在返回元组的第二项中返回。
$ python test_getopt.py -d d_value o --pattern -t
[('-d', 'd_value')]
['o', '--pattern', '-t']
类似地,longopts参数表示需要解析的长参数。
列表中的每一个字符串代表一个长参数:
import sys
import getopt
opts, args = getopt.getopt(sys.argv[1:], '', ["author", "country"])
print(opts)
print(args)
# $ python test_getopt.py --author --country
# [('--author', ''), ('--country', '')]
# []
要解析带有参数值的长参数,还应在每个长参数后附带一个等于号(=),以标识该参数需要带值:
import sys
import getopt
opts, args = getopt.getopt(sys.argv[1:], '', ["author=", "country"])
print(opts)
print(args)
# $ python test_getopt.py --author justdopython --country
# [('--author', 'justdopython'), ('--country', '')]
# []
所以最终就得到了我们一开始的解析结果:
import sys
import getopt
opts, args = getopt.getopt(sys.argv[1:], 'd:t:', ["author=", "country=", "auto"])
print(opts)
print(args)
# $ python test_getopt.py -d today -t now --author justdopython --country China --auto
# [('-d', 'today'), ('-t', 'now'), ('--author', 'justdopython'), ('--country', 'China'), ('--auto', '')]
# []
解析完成后,我们再从opts中提取相应的值即可。
懒人福音
getopt除了替我们节省了编写命令行参数解析代码的时间和精力,另一方面还可以让你在输入命令行参数时少打几个字母——当然,严谨来讲,我们并不建议此类行为。慎用,慎用!
getopt对长参数的解析支持前缀匹配,只要输入的参数能够与某个指定参数唯一匹配,同样能够完成预期解析。
$ python test_getopt.py -d today -t now --auth justdopython --coun China --auto
[('-d', 'today'), ('-t', 'now'), ('--author', 'justdopython'), ('--country', 'China'), ('--auto', '')]
[]
可以看到,author和country两个参数我们都只输入了一部分,但是getopt依然进行了正确的解析。
来源:https://juejin.cn/post/7108908752174579743


猜你喜欢
- python读取.txt(.log)文件 、.xml 文件 、excel文件数据,并将数据类型转换为需要的类型,添加到list中详解1.读取
- 本文实例讲述了Python实现读取txt文件中的数据并绘制出图形操作。分享给大家供大家参考,具体如下:下面的是某一文本文件中的数据。6.11
- 1、前言 MySQL 是完全网络化的跨平台关系型数据库系统,同时是具有客户机/服务器体系结构的分布式数据库管理系统。它具有功能强、使用简便、
- 一、前言构建命令行程序很酷:命令行可以按照我们的设定完成相应的工作,相比 GUI 界面程序,无需花费大量时间设计 GUI 界面。但要使命令行
- 因为固体物理书上的球面投影图太难看,就自学 javascipt 用 echarts 做了个可交互的,效果如下:上面为立方晶系主要晶面(晶向)
- 目录1. 序列2. 列表2.1 列表的特性2.1.1 列表的连接操作符和重复操作符2.1.3 列表的索引2.1.4 列表的切片2.1.5 列
- 快速排序(QuickSort)是对冒泡排序的一种改进:基本思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一
- 我们知道分析MySQL语句查询性能的方法除了使用EXPLAIN 输出执行计划,还可以让MySQL记录下查询超过指定时间的语句,我们将超过指定
- 有效地加载数据有时我们需大量地把数据加载到数据表,采用批量加载的方式比一个一个记录加载效率高,因为MySQL不用每加载一条记录就刷新一次索引
- 本文实例为大家分享了python3实现证件照背景替换的具体代码,供大家参考,具体内容如下import cv2import numpy as
- 这篇文章主要介绍了python 采用paramiko 远程执行命令及报错解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的
- 我想从文件读数据的话,要比直接从数据库快一些吧(个人观点),昨天测试了读出6000条新闻,读数据库用了23579毫秒,读文件只用了123毫秒
- python对XML文件的操作1、xml 创建import xml.etree.ElementTree as ETnew_xml=ET.El
- 最近我看到看到使用python实现火车票查询,我自己也实现了,感觉收获蛮多的,下面我就把每一步骤都详细给分享出来。(注意使用的是python
- 在SQL Server 中每个变量、参数、表达式等都有数据类型。系统提供的数据类型分为几大类,如表4-2 所示。&
- 本文实例讲述了Python网络编程基于多线程实现多用户全双工聊天功能。分享给大家供大家参考,具体如下:在前面一篇《Python网络编程使用s
- 从 webpack book 的 Loading Assets 一章中延申出来。改善前端项目体验中,很重要的点就是静态资源的优化。它是由于浏
- 注意:安装opencv之前需要先安装numpy,matplotlib等一、安装方法方法一、在线安装1.先安装opencv-pythonpip
- 为了组织search线的设计师交流会,特地去准备了一些资料。《SERP 2010》是其中一个,但是由于时间关系没有进行讨论。原著是英文报告,
- 我们知道Vscode是一款强大的编辑器,我们可以通过商城里面的插件扩展来写C/C++/python/java等。同样Vscode支持SQL语