Python标准库—optparse命令行选项解析

 2016年12月31日 19:56   Nick王   开发    1 评论   298 浏览 

optparse是一个方便的、灵活的和功能强大的Unix/Posix 规范的命令行选项库。


简单示例

$cat opt.py
#!/usr/bin/env python
# coding=utf-8
from optparse import OptionParser

parser = OptionParser()
parser.add_option("-f", "--file", dest="filename", help="write report to FILE", metavar="FILE")
parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=True, help="don't print status messages to stdout")

(options, args) = parser.parse_args()

查看帮助信息
$python opt.py --help
Usage: opt.py [options]

Options:
  -h, --help            show this help message and exit
  -f FILE, --file=FILE  write report to FILE
  -q, --quiet           don't print status messages to stdout


选项使用方法

opt.py --file=outfile -q  
opt.py -f outfile --quiet  
opt.py --quiet --file outfile  
opt.py -q -foutfile  
opt.py -qfoutfile


建立解析器

需要导入OptionParser类,然后创建一个OptionParser实例

from optparse import OptionParser
...
parser = OptionParser()

optparse.OptionParser类创建时可以不提供任何参数,不过标准库为我们提供了很多有用的参数可以初始化很多有用的信息。

  • usage参数:默认值是'%prog [options]',用于在命令行解析出错或者显示帮助信息时使用,比如'%prog [options] arg1 arg2'

  • prog参数:usage参数中的prog可以单独指定,用于替换version和usage里面的prog,比如 prog = "foo"

  • 如果不想显示usage部分,可以给usage赋值optparse.SUPPRESS_USAGE

  • option_class参数:定义向解析器用add_option方法添加选项时采用的类,一般采用默认的optparse.Option类就行,或者自己重新定义一个。

  • version参数:如果使用这个参数,生成的命令行会增加一个--version参数,例如python --version可以显示命令行的版本,这个参数可以使用prog参数,如version = '%prog 1.0'

  • descripiton参数:用于简洁的描述命令行的作用,位置位于usage和option list的显示之间

  • add_help_option参数:默认为True,会自动生成一个-h选项打印帮助信息。如果赋值为False,则不会生成-h选项

  • epilog参数:用于对各个选项参数做一个简短的说明,显示位置位于option list下面

  • option_list参数:由option实例组成的列表,它们定义了命令行格式的构成;比如parser = optparse.OptionParser(option_list = [optparse.make_option('-n', '--name', dest='name', action = 'store')])

  • conflict_handler参数:当选项冲突的时候要怎么做


命令行构成

有了解析器,接下来就可以开始定义选项,他的基础语法如下:

parser.add_option(opt_str, ..., attr=value, ...)

每个选项都有一个或者是多个的选项字符串(opt_str),和几个选项属性。



通常情况下,每个选项都会有一个短选项和一个长选项,比如:

parser.add_option("-f", "--file", ...)

你可以随便定义几个短选项或者几个长选项,这都是可以的。但是必须要有一个完整明确的选项。



当定义完所有的选项,通知optparse来解析你程序的命令行选项:

(options, args) = parser.parse_args()

parse_args()返回两个值:

  • options, 一个包含所有选项的的对象。比如当接受到一个 --file的选项的时候,option.file 将会是用户提供的文件名,如果用户没有提供文件名则为None。

  • args, 是在命令行选项后面剩余的位置参数的列表



除了选项字符串(opt_str)还有四个部分,分别是action、type、dest、help;而action是最根本的。



什么是action?

action告诉optparse当遇到命令行选项的时候应该做什么。如果你不指定一个action选项,optparse默认为store。


store action

多数的命令选项中的action为store。看下面例子:

$cat opt1.py
#!/usr/bin/env python
# coding=utf-8
from optparse import OptionParser

parser = OptionParser()
parser.add_option("-f", "--file", action="store", type="string", dest="filename")
args = ["-f", "foo.txt"]
(options, args) = parser.parse_args(args)
print(options.filename)

执行结果
$python opt1.py
foo.txt

等同于

$cat opt1.py
#!/usr/bin/env python
# coding=utf-8
from optparse import OptionParser

parser = OptionParser()
parser.add_option("-f", "--file", action="store", type="string", dest="filename")
(options, args) = parser.parse_args()
print(options.filename)

执行结果
$python opt1.py -f foo.txt
foo.txt

当optparse解析到选项字符串-f的时候,会将它后面的参数foo.txt存储到options.filename 。当调用parse_args()后options.filename的值就为foo.txt



当然也可以指定其他的type,比如:

parser.add_option("-n", type="int", dest="num")

注意这里是没有长选项的,这样做是完全合理的。并且没有明确的指出action,因为action的默认值就为store。



如果不指定type,那么optparse默认会设置为string;再结合默认的action为store;所以最简答的例子如下:

parser.add_option("-f", "--file", dest="filename")

如果你连dest都不指定的话,optparse会这样做:如果第一个长选项字符串为--foo-bar,那么默认的dest为foo-bar;如果没有长选项,optparse会找到第一短选项,-f的dest就为f。



处理布尔选项

设置一个变量为真或者为假这样的需求是很常见的。optparse有两个选项支持做这些事情:store_true和store_false。比如有一个verbose的选项,可以使用-v开启或者使用-q关闭:

parser.add_option("-v", action="store_true", dest="verbose")
parser.add_option("-q", action="store_false", dest="verbose")

在这个例子中有两个选项指向了同一个dest,这是完全可以的。当optparse遇到命令行参数中的-v的时候options.verbose的值为true,当optparse遇到命令行参数中的-q的时候options.verbose的值为false。



其他的action

optparse也支持一些其他的actions:

  • store_const: 存储一个恒定的值

  • append: 追加选项的参数到一个列表

  • count: 增长一个计数器

  • callback: 调用一个指定的函数



设置默认值

考虑上面的例子,如果我想要设置verbose默认为true,除非看到-q选项,那么我们就可以设置一个默认值:

parser.add_option("-v", action="store_true", dest="verbose", default=True)
parser.add_option("-q", action="store_false", dest="verbose")

这等价于

parser.add_option("-v", action="store_true", dest="verbose")
parser.add_option("-q", action="store_false", dest="verbose", default=True)

因为他们有完全一样的dest。



还有另外一个更加整洁的方法是使用set_defaults()方法。可以在parse_call()调用之前的任何地方使用它:

parser.set_defaults(verbose=True)
parser.add_option(...)
(options, args) = parser.parse_args()



生成帮助信息

optparse能使用命令行接口自动的生成帮助信息和使用方法。我们要做的只需要给每个选项提供一个help的值:

usage = "usage: %prog [options] arg1 arg2"
parser = OptionParser(usage=usage)
parser.add_option("-v", "--verbose",
                  action="store_true", dest="verbose", default=True,
                  help="make lots of noise [default]")
parser.add_option("-q", "--quiet",
                  action="store_false", dest="verbose",
                  help="be vewwy quiet (I'm hunting wabbits)")
parser.add_option("-f", "--filename",
                  metavar="FILE", help="write output to FILE")
parser.add_option("-m", "--mode",
                  default="intermediate",
                  help="interaction mode: novice, intermediate, "
                       "or expert [default: %default]")

当optparse在命令行遇到-h或者--help,或者是调用parse.print_help(),那么会在标注输出打印以下内容:

Usage: <yourscript> [options] arg1 arg2

Options:
  -h, --help            show this help message and exit
  -v, --verbose         make lots of noise [default]
  -q, --quiet           be vewwy quiet (I'm hunting wabbits)
  -f FILE, --filename=FILE
                        write output to FILE
  -m MODE, --mode=MODE  interaction mode: novice, intermediate, or
                        expert [default: intermediate]

如果触发help信息,那么程序会在打印完help信息之后退出。


在这个脚本中我们使用了自己定义的usage message:

usage = "usage: %prog [options] arg1 arg2"

如果不指定usage string,optparse会使用默认的"Usage: %prog [options]"


每个选项只要定义了help string就不需要关心其他了,optparse会帮助我们很好的打印输出。


在自动生成帮助信息的时候还可以生成它们所需要的值,比如:

-m MODE, --mode=MODE

这里的"MODE"被叫做meta-variable:它代表-m/--mode所期望接受的值。默认情况下optparse将meta-variable的值转为大写。有的时候这并不是你想要的,比如--filename选项设置metavar="FILE",那么它自动生成的结果如下:

-f FILE, --filename=FILE



选项组grouping options

当处理很多选项的时候,为了更好的输出help信息,可以给这些选项分组;一个OptionParser可以包含多个option groups,每个选项组可以包含多个option。

class optparse.OptionGroup(parser, title, description=None)
  • parser是一个OptionParser的实例

  • title是组标题

  • description是组的一个长的描述信息


OptionGroup继承自OptionContainer(OptionParser也是继承自OptionContainer),所以可以使用add_option方法添加一个选项到group。

一旦所有的选项全部都定义完毕,就可以使用OptionParser的add_option_group()方法将group添加到先前定义的parser。



继续前面的例子,添加一个option group是非常容易的:

group = OptionGroup(parser, "Dangerous Options",
                    "Caution: use these options at your own risk.  "
                    "It is believed that some of them bite.")
group.add_option("-g", action="store_true", help="Group option.")
parser.add_option_group(group)

会有如下的输出

Usage: <yourscript> [options] arg1 arg2

Options:
  -h, --help            show this help message and exit
  -v, --verbose         make lots of noise [default]
  -q, --quiet           be vewwy quiet (I'm hunting wabbits)
  -f FILE, --filename=FILE
                        write output to FILE
  -m MODE, --mode=MODE  interaction mode: novice, intermediate, or
                        expert [default: intermediate]

  Dangerous Options:
    Caution: use these options at your own risk.  It is believed that some
    of them bite.

    -g                  Group option.

一个更加完整的例子可能会有多个group,继续扩展上面的例子

group = OptionGroup(parser, "Dangerous Options",
                    "Caution: use these options at your own risk.  "
                    "It is believed that some of them bite.")
group.add_option("-g", action="store_true", help="Group option.")
parser.add_option_group(group)

group = OptionGroup(parser, "Debug Options")
group.add_option("-d", "--debug", action="store_true",
                 help="Print debug information")
group.add_option("-s", "--sql", action="store_true",
                 help="Print all SQL statements executed")
group.add_option("-e", action="store_true", help="Print every action done")
parser.add_option_group(group)

最后的输出结果

Usage: <yourscript> [options] arg1 arg2

Options:
  -h, --help            show this help message and exit
  -v, --verbose         make lots of noise [default]
  -q, --quiet           be vewwy quiet (I'm hunting wabbits)
  -f FILE, --filename=FILE
                        write output to FILE
  -m MODE, --mode=MODE  interaction mode: novice, intermediate, or expert
                        [default: intermediate]

  Dangerous Options:
    Caution: use these options at your own risk.  It is believed that some
    of them bite.

    -g                  Group option.

  Debug Options:
    -d, --debug         Print debug information
    -s, --sql           Print all SQL statements executed
    -e                  Print every action done



打印版本信息

和usage message 一样,你可以在创建 OptionParser 对象时,指定其 version 参数:

parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.0")

你的脚本可以这样调用

$ /usr/bin/foo --version
foo 1.0

下面的两个方法也可以被用来打印或者获取版本信息:

OptionParser.print_version(file=None)
OptionParser.get_version()



optparse的异常处理

optparse有两大类错误,程序的错误和用户的错误。程序的错误通常是不正确的调用OptionParser.add_option()。这类程序的错误通常都是raise 一个 异常让程序崩溃。


这里关心的是如何处理用户的错误。optparse能自动的处理一些用户的错误比如错误的选项、丢失的选项等。你还可以自己调用OptionParser.error():

(options, args) = parser.parse_args()
...
if options.a and options.b:
    parser.error("options -a and -b are mutually exclusive")

在这个例子中程序会打印用法信息和错误信息到标准错误并且以错误状态退出程序。


当传递4x给一个选项以整数处理的时候会报如下错误:

$ /usr/bin/foo -n 4x
Usage: foo [options]

foo: error: option -n: invalid integer value: '4x'

或者当没有给定参数的时候会报如下错误:

$ /usr/bin/foo -n
Usage: foo [options]

foo: error: -n option requires an argument



optparse命令行选项的排序(源码来自ansible)

首先来一个简单的例子:

$cat opt1.py
#!/usr/bin/env python
# coding=utf-8
import optparse
parser = optparse.OptionParser()

parser.add_option("-f", "--file", action="store", type="string", dest="filename")
parser.add_option("-a", "--all", action="store_false", dest="all", help="ooooooooo")

(options, args) = parser.parse_args()
print(options.filename)

执行结果

$python opt1.py -h
Usage: opt1.py [options]

Options:
  -h, --help            show this help message and exit
  -f FILENAME, --file=FILENAME
  -a, --all             ooooooooo


使用排序的命令行选项

$cat opt1.py
#!/usr/bin/env python
# coding=utf-8
import optparse
import operator

class SortedOptParser(optparse.OptionParser):
    def format_help(self, formatter=None, epilog=None):
        self.option_list.sort(key=operator.methodcaller('get_opt_string')) ##这里是排序关键,使用OptionParser.get_opt_string()的结果进行排序
        return optparse.OptionParser.format_help(self, formatter=None)

parser = SortedOptParser()

parser.add_option("-f", "--file", action="store", type="string", dest="filename")
parser.add_option("-a", "--all", action="store_false", dest="all", help="ooooooooo")

(options, args) = parser.parse_args()
print(options.filename)

执行结果

$python opt1.py -h
Usage: opt1.py [options]

Options:
  -a, --all             ooooooooo
  -f FILENAME, --file=FILENAME
  -h, --help            show this help message and exit



官方文档的一个完整的例子

from optparse import OptionParser
...
def main():
    usage = "usage: %prog [options] arg"
    parser = OptionParser(usage)
    parser.add_option("-f", "--file", dest="filename",
                      help="read data from FILENAME")
    parser.add_option("-v", "--verbose",
                      action="store_true", dest="verbose")
    parser.add_option("-q", "--quiet",
                      action="store_false", dest="verbose")
    ...
    (options, args) = parser.parse_args()
    if len(args) != 1:
        parser.error("incorrect number of arguments")
    if options.verbose:
        print "reading %s..." % options.filename
    ...

if __name__ == "__main__":
    main()



使用optparse和argparse兼容的方法写的一个端口扫描工具(在较老的Python上只有argparse)

#/usr/bin/env python
# -*- coding:utf-8 -*-
__author__ = 'bigtree'

import sys
import socket

class Scan_Port(object):
    '''TCP Port Scan.'''
    def __init__(self, ip_addr, start_port, end_port, *port_numbers):
        self.ip_addr = ip_addr
        self.start_port = start_port
        self.end_port = end_port
        self.port_numbers = port_numbers

    def result(self):
        '''If the port list is specified, the port specified in the list is scanned.'''
        Flag = 1
        if self.port_numbers:
            for port in self.port_numbers:
                self.__check(port, Flag)
        else:
            Flag = 0
            for port in xrange(self.start_port, self.end_port+1):
                self.__check(port, Flag)

    def __check(self, port, Flag):
        '''Scan the specified port.'''
        if self.__checkport(port):
            if self.__tcpconnport(port):
                print("PORT: %5s/tcp STATE: open" % port)
            else:
                if Flag == 1:print("PORT: %5s/tcp STATE: closed" % port)

    def __tcpconnport(self, port):
        '''Try to connect the specified port.'''
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect((self.ip_addr, port))
            s.close()
            return True
        except socket.error:
            return False

    def __checkport(self, port):
        '''Check port is valid'''
        if port >=1 and port <= 65535:return True

if __name__ == '__main__':
    try:
        parser_module = __import__("argparse")
        parserobj = getattr(parser_module, "ArgumentParser")(description = "ScanPort  0.1", epilog = "bigtree6688@hotmail.com")
        addfunc = getattr(parserobj, "add_argument")
    except ImportError:
        parser_module = __import__("optparse")
        parserobj = getattr(parser_module, "OptionParser")(description = "ScanPort  0.1", epilog = "bigtree6688@hotmail.com")
        addfunc = getattr(parserobj, "add_option")
    except Exception,e:
        print(e)
        sys.exit(1)

    addfunc("-s", "--start", help = "Start Port Number", type = int, default = 1, dest = "start_port", metavar = "SPort")
    addfunc("-e", "--end", help = "End Port Number", type = int, default = 65535, dest = "end_port", metavar = "EPort")
    addfunc("-n", "--num", help = "Scan Specified Port;If specified, the highest priority.", nargs = "+", type = int, dest = "port_num", metavar = "Ports")
    addfunc("-a", "--address", help = "Server IP Address", default = "127.0.0.1", dest = "ipaddr", metavar = "Address")
    if isinstance(parserobj.parse_args(), tuple): #optparse return tuple, argparse return dict
        args = parserobj.parse_args()[0]
    else:
        args = parserobj.parse_args()

    if args.port_num:
        Scan = Scan_Port(args.ipaddr, args.start_port, args.end_port, *args.port_num)
    else:
        Scan = Scan_Port(args.ipaddr, args.start_port, args.end_port)

    Scan.result()

执行结果

$python ScanPort.py -h
usage: ScanPort.py [-h] [-s SPort] [-e EPort] [-n Ports [Ports ...]]
                   [-a Address]

ScanPort 0.1

optional arguments:
  -h, --help            show this help message and exit
  -s SPort, --start SPort
                        Start Port Number
  -e EPort, --end EPort
                        End Port Number
  -n Ports [Ports ...], --num Ports [Ports ...]
                        Scan Specified Port;If specified, the highest
                        priority.
  -a Address, --address Address
                        Server IP Address

bigtree6688@hotmail.com


$python ScanPort.py -a 127.0.0.1 -n 22
PORT:    22/tcp STATE: open





如无特殊说明,文章均为本站原创,转载请注明出处