Python3+getopt/argparse解析命令行参数

一、说明

在学C语言的时候就知道可以通过argc获取命令行参数个数,可以通过argv获取具体参数。但自己写的程序获取到的参数一是没有键值形式二是写的参数不能乱序,和系统命令不太一样。

但这种位置参数用起来还是很不方便的,还是很有必要弄清系统命令的那种参数该如何实现。这里我们介绍经典的getopt和pythonic的argparse。

 

二、getopt实现

2.1 程序代码

此程序中设置-h/-n/-p三个选项,-h不带值-n和-p带值;三个参数设置等价长格式--help/--name/--profession。

程序通过sys.argv[1:]来获取所有待解析参数,然后传到getopt进行解析,更多说明见代码注释。

import getopt
import sys


def _parse_option():
    try:
        # 第一个参数argv----传过来的要解析的参数数组。形如['-n', 'ls', '-p', 'programmer']
        # 第二个参数"hn:p:"----用于向getopt注册短格式。没有:表示该参数不带值,有:表示下一参数为该参数的值
        # 第三个参数[]----用于向getopt注册长格式。没有=表示该参数不带值,有=表示=号后边为其值(如果没有=号就以下一个参数为其值)
        # 第三个参数[]----[]不是可选的意思,这里是代码,[]表示该参数是个数组
        # opts----以元组形式存放解析出的参数。形如[('-n', 'ls'), ('-p', 'programmer'), ('-h', '')]
        # args----以数组形式存放按所有注册的格式未能解析参数
        # 系统参数可通过sys.argv[index]来获取,sys.argv[0]是本身文件名
        print(f"will parse argv: {sys.argv[1:]}")
        opts, args = getopt.getopt(sys.argv[1:], "hn:p:", ["help", "name=", "profession="])
        print(f"parsed argv: opts----{opts} args----{args}")
    except getopt.GetoptError:
        # 参数不符合注册格式要求报错
        print("parameter format error")
        _usage()
        sys.exit(2)

    options_dict = {}
    # 遍历所有元组
    # getopt只会严格按照注册的格式解析参数,而不理解哪个短格式与哪个长格式等价,等价是我们这里设定短格式和长格式用同一响应造成的
    # 也就是说getopt并不理解-n和--name等价,他有-n就解析-n有--name就解析--name,两个都有就两个都解析。-n和--name等价是因为我们对这两个参数用同样的代码进行处理。
    # 比如执行python getopt_test.py -n ls --name=root,解析出的就是[('-n', 'ls'), ('--name', 'root')]
    for opt, arg in opts:
        # -h与--help等价
        if opt in ("-h", "--help"):
            _usage()
            sys.exit()
        # -n与--name等价
        elif opt in ("-n", "--name"):
            options_dict["user_name"] = arg
        # -p与--profession等价
        elif opt in ("-p", "--profession"):
            options_dict["user_profession"] = arg
    return options_dict, args


def _usage():
    print("getopt_test version 1.0")
    print("-h   print this message")
    print("-n   <your name>     equ --name")
    print("-p   <your profession>   equ --profession")


class TestGetopt:
    """
    sys.argv[index]武断地以空格来划分参数,并不能区分选项和选项值
    sys.argv[index]不能乱序,取第一个参数为用户名,就必须在第一个参数输入用户名,不能在第二或别的地方输
    我们使用getopt模块来解决这两个问题
    """
    def __init__(self):
        self.options_dict, self.args = _parse_option()

    def test_use_arg(self):
        print(f"options: {self.options_dict}")
        print(f"args: {self.args}")
        print(f"you are {self.options_dict['user_profession']} {self.options_dict['user_name']}!")


if __name__ == "__main__":
    obj = TestGetopt()
    obj.test_use_arg()
View Code

 

2.2 运行截图

 下图中依次以以下四种形式运行程序,观察getopt解析前后参数可对getopt有更直观理解

python getopt_test.py -h
python getopt_test.py -n ls -p programmer
python getopt_test.py -n ls -p programmer --name=lsx
python getopt_test.py -n ls -p programmer --name=lsx other1 other2

 

三、argparse实现

3.1 程序代码

python的getopt感觉就是C的封装,在使用上C的getopt类似,或者换言之还是比较麻烦的。我们下边使用argparse实现类似的功能。

直观感受上我们可以看到同样的功能代码量的减少,但其实更重要的是argparse多处理了很多我们没注意的细节,实际代码中也更推荐使用argparse。

另外python 3.2之前的版本参数解析库是optparse,用法和argparse类似且会被取代就不介绍了。

import argparse


def _parse_option():
    """所有要解析参数的文件加上这个函数"""
    parser = argparse.ArgumentParser()
    # 默认会自动添加-h参数,-h的作用是打印各参数说明并使用exit退出。不想要原始的-h可设置add_help=False指示不自动添加
    # parser = argparse.ArgumentParser(add_help=False)
    # parser.add_argument()支持参数及各参数说明如下
    # 开头的非关键字参数--可以是-开头的短格式,也可以是--开头的长格式。
    #   --其中短格式也支持-ltnp等多个参数连写的形式,但这多个连写的参数必须都已注册,不然程序会直接报错退出
    # action--参数操作动作
    #   --store(赋值)/store_const(赋给定的常量值)/store_true(如果命令中有该参数赋true)/store_false(如果命令中无该参数赋false)/append(多次出现时拼成列表)/
    #   --count(赋出现的次数)/help(打印帮助)/version(打印--version参数)/extend(存成列表)。默认为store
    # nargs--消费其后多少个参数作为其值。不同参数操作动作默认消费个数不同
    # const--当action为store_const时,要赋值变量的常量值
    # default--当命令行没有该参数时,该参数的默认值。默认为None
    # type--变量类型。可以是所有python的变量类型,默认是str
    # choices--可给该参数传的值的可选列表
    # required--参数是否必须。默认为false
    # help--参数描述信息
    # metavar--参数赋值形式示例
    # dest--参数解析后赋值到的变量。默认先赋给dest,没有则赋给长格式,没有再赋给短格式
    parser.add_argument("-n", "--name",
                        metavar="NAME", dest="user_name", default="ls", type=str, help="get user name")
    parser.add_argument("-p", "--profession",
                        metavar="PROFESSION", dest="user_profession", help="get user profession")
    # options--argparse.Namespace类型,存储解析到的参数
    # args--list类型,存储其他无法解析的参数
    # parse_args()方法在遇到没有承接方的参数时会报错error: unrecognized arguments:并直接exit退出
    # options = parser.parse_args()
    # args = None
    # parse_known_args()方法在遇到未注测的参数时会把它放入args而不是报错退出
    (options, args) = parser.parse_known_args()
    # 默认解析sys.argv[1:],所以并不需要自己把sys.argv[1:]传过去。当然你也可以传入自己想解析的参数列表
    # (options, args) = parser.parse_known_args(["need", "too", "parse", "param", "list"])
    return options, args


class TestArgparse:
    """所有要解析参数的类调用该函数进行解析即可"""

    def __init__(self):
        # options是optparse.Values类型并不是字典类型。optparse.Values类型可通过optparse.dest实现访问
        # args是列表类型
        self.options, self.args = _parse_option()

    def test_use_arg(self):
        print(f"options: {self.options}")
        print(f"args: {self.args}")
        print(f"you are {self.options.user_profession} {self.options.user_name}!")
        

if __name__ == "__main__":
    obj = TestArgparse()
    obj.test_use_arg()
View Code

 

3.2 运行截图

 

参考:

http://www.runoob.com/python3/python3-command-line-arguments.html

https://docs.python.org/3/library/argparse.html

https://stackoverflow.com/a/12818237

posted on 2018-10-16 18:07  诸子流  阅读(5202)  评论(0编辑  收藏  举报