Python随笔Python语言与信息数据获取和机器学习Pythoner集中营

python解析命令行

2016-11-13  本文已影响1164人  顾慎为

getopt:和C中的getopt()等价。
optparse:2.7后已不推荐使用。
argparse:基于optparse的新库。
docopt:根据文档描述,自动生成。另一份参考文档:docopt

更详细的内容可参考上述文档。

getopt


若对C的getopt()函数不熟悉,或者倾向于使用较少的代码,或者需要对帮助信息和错误信息有更高要求,以上情况优先使用argparse
该模块主要提供两个函数,以及一个异常判断。

getopt.getopt(args, options[, long_options])
args是需要解析的列表,通常情况下,是sys.argv[1:]
options是解析列表时使用的范式,如果某一项(单个字母)后面需要参数,则在此项后添加':'
long_options必须是字符串列表,字符串开头的'--'不能包括,若某一项后面需要参数,则在此项后添加'='。可选型的参数是不支持long_options的。是根据输入是否是字符串列表中唯一一项的前缀来匹配的。
返回值分为两个部分。第一部分是(option, value)格式的列表,第二部分是解析完后剩余部分。

getopt.gnu_getopt(args, options[, long_options])
作用同上。
区别在于getopt()一旦遇到未配置的选项,就会停止解析。而此函数使用GNU规则,即已配置的选项和未配置的选项是可以混杂的。

exception getopt.GetoptError**
当一个无法识别的选项,或者一个必须要有参数传入的选项未传入参数时,抛出此异常。
exception getopt.error**
GetoptError的旧名,为了向后兼容而保留。

具体实例可参考文档,不推荐使用。

argparse


一个简单例子:

import argparse

parser = argparse.ArgumentParser(description='Example with long option names')

parser.add_argument('--noarg', action="store_true", default=False)
parser.add_argument('--witharg', action="store", dest="witharg")
parser.add_argument('--witharg2', action="store", dest="witharg2", type=int)

print parser.parse_args(['--noarg', '--witharg', 'val', '--witharg2=3'])

argparse中,最常用的就是上述三部分了:创建一个ArgumentParser对象;使用add_argument()方法来为创建的ArgumentParser对象添加argument的解析规则;最后调用parse_args()来解析传入的内容,依据的是第二步制定的规则,生成的是一个Namespace对象,若未传参数给parse_args(),那么默认从sys.argv来获取命令行入参。

class argparse.ArgumentParser(prog=None, usage=None, description=None, epilog=None, parents=[],formatter_class=argparse.HelpFormatter, prefix_chars='-', fromfile_prefix_chars=None, argument_default=None,conflict_handler='error', add_help=True)
prog:程序的名字,默认是argv[0]。若设置,则在帮助信息中,可以使用%(prog)s来作为格式化的引用(修改一处全局受用)。
usage:帮助信息的usage字段,描述程序的各种用法,默认情况下会依据add_argument()来自动生成。
description:一个简单描述程序主要干啥以及怎么用的字符段。位于usage字符段和optional arguments字符段之间。
epilogoptional arguments字符段之后的字符段。
parents:继承的父parser,为了避免一些公共的内容重复定义,父parser在初始化时会设置add_help=False,这是为了防止出现父与子parser的-h冲突而抛出异常。
formatter_class:对于help输出进行格式化,除了输出的样式外,如果设置为ArgumentDefaultsHelpFormatter,则会自动在help输出中添加已定义的default值。
prefix_chars:options前的字符,默认为'-',可以添加其他字符,如'-+',但是如果没有包括'-',那么对应的option如'-h'就无法解析。
fromfile_prefix_chars:有时会使用文件给parse_args()传入参数,为了能够识别文件字符串,如"demo.txt",需要设置此值,如"@",那么所有以此字符为开头的字符串都被当作是文件,所以传给parse_args()的参数应该是@demo.txt。在该文件中,一行只能有一个参数。如文件中的'-f\nbar'会被解析成['-f','bar']
argument_default:一般情况下,默认值使用add_argument()来添加,或者使用set_defaults()设置一些键值对来添加。剩下一种情况就是设置此项(此处没看明白是咋回事)。
conflict_handler:解决在add_argument()阶段有冲突的option的依据策略,默认为error即抛出异常。一般情况下遇到冲突是抛出异常即可,但是如果设置了parents,那么需要重写父parser中的规则的时候,就需要将此项设置为resolve,但是重写是精确匹配的,如老规则定义了-h/--help,重写了-h,那么--help还是老规则。
add_help:是否添加-h/--helpoption,默认为True。为False时,是要做parent(其实可以设置子Parser重写)。默认是-h/--help,若prefix_chars中没有包含'-',那么就以其中第一个字符作为代替。

ArgumentParser.add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][,metavar][, dest])
name or flags:是位置参数,则需要传入名字;要是可选参数,则需要进行定义,如'-f','--foo'。
action:定义传入的参数如何处理。

nargs:通常一个选项后跟一个参数,通过设置此项可以实现不同情况。

如果nargs没有定义,则可传入参数的数量由action决定,通常情况下为一个,并且不会生成长度为一的列表。
const,一种是定义action='store_const'action='append_const'时使用。一种是定义nargs='?'时,可选项出现在命令行中,但之后并没有跟随赋值的参数,作为默认值传给此可选项。
default:默认值。
如果是一个字符串,那么Parser解析的时候会将它作为命令行传入值,使用type的值来进行转换类型,但是如果不是的话,就会使用定义的值而不进行类型转换。
如果设置了nargs='?'nargs='*',那么当没有参数赋值给该项时,会使用default定义的值。
default=argparse.SUPPRESS时,则表示命令行中未出现某一项时,不会对它进行默认赋值。
type:用于类型检查和类型转换。
使用FileType可简化对文件的操作。
还可以自定义函数,输入是一个字符串,输出是转换后的字符串。
当设置choices的时,类型检查会变得容易,因为只需要在一个范围内比较即可。
choices:给定了取值范围,超出会报错。
type也有定义时,会先使用type进行类型检查,所以choices中的取值必须符合type的定义,否则在parse_args()时会报错。
任何支持in操作符的均可作为choices的赋值,所以字典,列表,集合,等等其他容器均都支持。
required:默认情况下,可选项(前面有'-')被认为并不一定需要出现在命令行参数中,但是如果设置了required=True的话,则必须出现。此类设置违背人的常识,应避免使用。
help:帮助信息。
之前提到的%(prog)s可用于此处程序名的格式化,此外,还有%(default)s格式化default的值,%(type)s格式化type的值。
设置为argparse.SUPPRESS可不显示帮助信息。
metavar:在Parser生成帮助信息时,需要有字符代表需要传入的值。(这一段和dest相同,使用的就是dest的值)如果是位置参数,则用它本身代替;如果是可选参数,则使用它的大写来代替。使用metavar可替换默认的字符。
dest:大部分的选项都需要通过命令行来给其赋值,这些值的名字通过dest来定义,默认的规则如同metavar中所述。

ArgumentParser.parse_args(args=None, namespace=None)
args转换为namespace对象的一个值。默认情况下,sys.argv赋值给args,一个空的Namespace对象会被创建。
解析时,会对传入的参数进行检查,若不符合要求就会报错。
一般情况下,会自动判断传入的值到底是一个可选参数,还是一个负数(都用'-'开头)。但有时位置参数的值必须是一个'-'开头的值,如'-f',那么使用parser.parse_args(['--', '-f'])'--'代表后续的所有传入值都需要看做是位置参数。
parse_args()会返回填充好的Namespace对象,若要将它变为字典,可使用Python自带的vars()

vars(...)
    vars([object]) -> dictionary   
    Without arguments, equivalent to locals().
    With an argument, equivalent to object.__dict__.

也可不使用Namespace,而是传入一个提前生成的对象。

其他
ArgumentParser.add_subparsers([title][, description][, prog][, parser_class][, action][, option_string][, dest][, help][, metavar])用来定义子Parser。
class argparse.FileType(mode='r', bufsize=None)用来给add_argument()中的type参数赋值。
ArgumentParser.add_argument_group(title=None, description=None)定义一个组。
ArgumentParser.add_mutually_exclusive_group(required=False)定义组中只能有一个选项出现。
ArgumentParser.set_defaults(kwargs)设置默认值。
ArgumentParser.get_default(dest)获取默认值。
ArgumentParser.print_usage(file=None)
ArgumentParser.print_help(file=None)
ArgumentParser.format_usage()
ArgumentParser.format_help()以上四种都是帮助信息相关的。
ArgumentParser.parse_known_args(args=None, namespace=None)只解析知道的部分。
ArgumentParser.convert_arg_line_to_args(arg_line)
ArgumentParser.exit(status=0, message=None)
ArgumentParser.error(message)

docopt


理念是好的帮助信息应该正好包含生成命令行解析器所需要的全部信息。

docopt.docopt(doc, argv=None, help=True, version=None, options_first=False)
doc参数可以是一个模块的docstring(__doc__),或者是其他符合格式的帮助信息。
argv默认情况下使用sys.argv[1:],或者可以使用一个字符串。
help默认为True,当输入-h--help后(需提前设置好),解析器能够自动生成对应的帮助文本。需要手动管理的可设置为False。
version版本信息,是可选参数。
options_first默认为False。当设置为True时,不允许可选参数和位置参数进行混合,即在出现的第一个位置参数之后的所有参数,均被当作是位置参数,这是为了和POSIX保持兼容。
返回值是一个字典,key是option,value就是对应输入的参数。

帮助信息格式分为两部分,Usage patternOption descriptions。只有符合格式的字符串才会被识别并解析,其余字符串会被忽略。
Usage pattern
举例:

Usage: my_program.py [-hso FILE] [--quiet | --verbose] [INPUT ...]

Usage pattern是doc的子字符串。开始于usage:(大小写不敏感),结束于一行空行。
位于usage:之后的第一个单词是程序的名字,可以重复多次进行不同pattern的定义。

"""
Usage: my_program.py FILE 
       my_program.py COUNT FILE
"""

每个pattern都由以下部分组成。
一个是<arguments>, ARGUMENTS格式的定义,要么是用尖括号包括,要么是大写。
还有一个是-options,必须要以“-”开头,可以堆栈好几个option,如-oiv等价于-o -i -v。option是可以有参数的,如-f FILE-f=FILE-fFILE,都是等价的。其他一些选择在下方的option descriptions中进行具体定义。
最后一个是commands,除了以上提及的两种情况,剩下的格式,再加上两个特殊的命令:单个的“-”和双个的“-”,这些都归属于此。

commands中,[ ]表示可选。( )表示必须存在,所有没有用[ ]包裹的字符串都默认被( )包裹。
|是管道符,若某一个选项必须存在,则将他们用( )包裹,如my_program.py(--clockwise | --counter-clockwise) TIME,若都是可选项,则用[ ]包裹,如my_program.py[--left | --right]
...表示有一个或多个元素,如my_program.py FILE ...表示一个或多个FILE可接受,而my_program.py [FILE ...]表示0个或多个FILE可接受。
[options](大小写敏感)是任意options的缩写。可以使用它来定义该pattern可以使用任何在下方Option descriptions中定义的option。
[--]是为了分隔可选参数和位置参数,如Usage: my_program [options] [--] <file>...,其中中括号可去除,变为必填项。
[-]若出现,则表示程序需要使用stdin,禁止使用file。而单个的-则是一个普通字符。

如果在usage pattern中重复出现同名位置参数多次,或者同名有参数的可选参数多次,那么传入的值会被解析成一个列表。如Usage: my_program.py <file> <file> --path=<path>...的结果会是args['<file>'] == ['file1', 'file2']以及args['--path'] == ['./here', './there']

Option descriptions
举例:

-h --help    show this
-s --sorted  sorted output
-o FILE      specify output file [default: ./test.txt]
--quiet      print less text
--verbose    print more text

此部分位于usage pattern下方,它可以有三方面作用:定义同义的短option和长option;某个option是否需要传入参数;某个option是否有默认参数。
规则如下:
每行定义一个option,必须以一个或两个“-”开头。
为定义需要传入的参数,可以在对应的option后方加上一个字符串,两者以空格或“=”相隔,两者都是可行的,但推荐只使用一种风格。

-o FILE --output=FILE       # without comma, with "=" sign
-i <file>, --input <file>   # with comma, wihtout "=" sign

使用两个空格来将option本身与它的描述想分隔(按照示例应该是至少两个空格)。

--verbose More text.   # BAD, will be treated as if verbose option had
                       # an argument "More", so use 2 spaces instead
-q        Quit.        # GOOD
-o FILE   Output file. # GOOD
--stdout  Use stdout.  # GOOD, 2 spaces

在option对应的描述后方进行默认参数的设置,格式是[default:<my-default-value>]

--coefficient=K  The K coefficient [default: 2.95]
--output=FILE    Output file [default: test.txt]
--directory=DIR  Some directory [default: ./]

若某option是可重复的,则默认参数会以空格作为分割符进行分割,生成一个字符串列表。否则,作为一个字符串整体。

docopt不适用于大型多层次的命令行参数解析(例如git,但是官方给出了一个example)。同时它没有对数据的校验功能,无法向用户报错。
使用try docopt可进行docopt在线命令行参数解析。

举例


例题是:知道创宇面试题
使用argparse

import argparse
from argparse import ArgumentDefaultsHelpFormatter

parser = argparse.ArgumentParser(prog="Spider", description='Knownsec Interview Spider By Docopt.', formatter_class=ArgumentDefaultsHelpFormatter)

parser.add_argument('-u', dest='url', action='store', required=True, help='the url the spider begins with')
parser.add_argument('-d', dest='deep', action='store', required=True, type=int, help='the depth of spider')
parser.add_argument('-f', dest='logfile', action='store', default='spider.log', help='the name of logfile')
parser.add_argument('-l', dest='loglevel(1-5)', action='store', type=int, choices=range(1,6), default=1, help='the details level of the log, range 1 to 5')
parser.add_argument('--testself', dest='testself', action='store_true', help='the self test by this program')
parser.add_argument('--thread', dest='thread_number', action='store', type=int, default=10, help="the thread pool's capacity")
parser.add_argument('--dbfile', dest='dbfile_filepath', action='store', required=True, help='the sqlite3 file path')
parser.add_argument('--key', dest='keyword', action='store', help='the keyword to search in webpage')


print parser.parse_args()

输出:

python argparse_demo.py -u 123.com -d 1 --dbfile 123.txt -f logfile -l 1 --testself --thread 2 
Namespace(dbfile_filepath='123.txt', deep=1, keyword=None, logfile='logfile', loglevel(1-5)=1, testself=True, thread_number=2, url='123.com')

体会就是功能很强大,实现很方便。所以我倾向于使用它。

使用docopt

"""
Knownsec Interview Spider By Docopt.

Usage:
  docopt_demo.py -u <url> -d <deep> [-f <logfile>] [-l <loglevel>] [--testself] [--thread <number>] --dbfile <filepath>  [--key=<keyword>]
  docopt_demo.py -h | --help
  docopt_demo.py --version

Options:
  -h --help               Show this screen.
  --version               Show version.
  -u <url>                the url the spider begins with
  -d <deep>               the depth of spider
  -f <logfile>            the name of logfile [default: spider.log]
  -l <loglevel>           the details level of the log, range 1 to 5 [default: 1]
  --testself              the self test by this program
  --thread <number>       the thread pool's capacity [default: 10]
  --dbfile <filepath>     the sqlite3 file path
  --key=<keyword>         the keyword to search in webpage

"""
from docopt import docopt

if __name__ == '__main__':
    arguments = docopt(__doc__, version='Spider 1.0')
    print arguments

输出:

python docopt_demo.py  -u 123.com -d 1 --dbfile 123.txt -f logfile -l 1 --testself --thread 2 

{'--dbfile': '123.txt',
 '--help': False,
 '--key': None,
 '--testself': True,
 '--thread': '2',
 '--version': False,
 '-d': '1',
 '-f': 'logfile',
 '-l': '1',
 '-u': '123.com'}

可见,如果选项不需要参数,则-h和--version这种没有设置的会赋值为False,而--testself会赋值为True;对于需要参数的选项,如果没有设置,如--key,会被赋值为None,而有设置并且给出参数的,会赋值对应的参数,如--dbfile。
那么,对于有设置但是没有给出参数的呢,并不会抛出异常报错,而是把Usage部分(有时加上Options部分)输出,真是太不友好了,debug的时候真是坑爹啊。

另外,使用docopt时候需要注意__doc__的用法,如果是写函数的__doc__,则在函数名之下开始写,而一个模块的则必须在开头写,也就是在import之前写,否则__doc__ == None


20161226
今天了解了一个很方便的库:click,作为补充。

上一篇下一篇

猜你喜欢

热点阅读