为程序添加一个优雅的command line interface

为程序编写command line interface总是一件麻烦的事,尤其是程序支持多个参数,参数之间还有关系的时候,就更为棘手了。当我在编写 mdtogh 这个工具时就遇到这个问题。

好在,python的优点之一就是第三方的模块特别多,而今天就要介绍用来解决这个问题的强大模块:

docopt

不是python用户?别急,docopt也port到其他语言上了,在文章最后有所介绍。先把介绍看完吧。

docopt

docopt是一个第三方开发的模块,用于创建command line interface。其完成的工作很简单:

处理命令行参数

其在PyCon UK 2012被介绍,您可以在youtube上看到相应的介绍。

特性从字符串中生成相应参数支持多种格式可选参数命令特定位置的参数自动生成帮助命令参数可以设定默认值及其他特性一个简单的例子

exam.py

import sysfrom docopt import docoptdoc='''Usage:    my_program tcp <host> <port> [--timeout=<seconds>]    my_program serial <port> [--baud=<n>] [--timeout=<seconds>]    my_program (-h | --help | --version)Options:    -h, --help  Show this screen and exit.    --baud=<n>  Baudrate [default: 9600]    --timeout=<seconds>  timeout [default: 50]'''def test(argv=None):    if argv is None:        argv = sys.argv[1:]    args = docopt(doc, argv=argv, version='0.01')    print args    passif __name__ == '__main__':    test()

运行结果:

summertekiMacBook-Pro:docopt summer$ python exam.pyUsage:    my_program tcp <host> <port> [--timeout=<seconds>]    my_program serial <port> [--baud=<n>] [--timeout=<seconds>]    my_program (-h | --help | --version)

帮助输出:

summertekiMacBook-Pro:docopt summer$ python exam.py -hUsage:    my_program tcp <host> <port> [--timeout=<seconds>]    my_program serial <port> [--baud=<n>] [--timeout=<seconds>]    my_program (-h | --help | --version)Options:    -h, --help  Show this screen and exit.    --baud=<n>  Baudrate [default: 9600]    --timeout=<seconds>  timeout [default: 50]

正确的输入:

summertekiMacBook-Pro:docopt summer$ python exam.py tcp 127.0.0.1 80{'--baud': '9600', '--help': False, '--timeout': '50', '--version': False, '<host>': '127.0.0.1', '<port>': '80', 'serial': False, 'tcp': True}

看到上面的程序及执行的结果,我们可以看到docopt已经很方便的帮我们创建了command line interface,并且是五脏俱全,几乎常用的cli写法都支持,很是方便。仅仅编写一个帮助文档,就能创建cli,的确轻松了不少。

安装

Python库的安装方式是我觉得最方便,也是最优雅的安装方法了。仅仅一个语句就搞定:

pip install docopt

That’s it!剩下的就全部交给pip来解决了。

详细介绍

通过上面简单的例子,可以看到docopt在实现command-line interface特别简单。

API

docopt仅仅提供了一个api。就是我们上面用的docopt。 查看其介绍,可以看到详细的介绍。

from docopt import docoptdocopt(doc, argv=None, help=True, version=None, options_first=False)

doc是必须的参数,而剩下的4个是可选的:

doc:为可以被翻译的字符串。一般来说包含UsageOptions两个字段.具体的介绍请参考下面。argv:可选的参数数组。docopt默认使用sys.argv[1:],不过您也可以进行修改。help:docopt会把doc作为help手册提供给用户,您也可以设置为False,然后自己处理。version:当用户输入--version时显示该数据。其值可以为任何printable对象,一般是字符串。options_first: 禁止混合options及positional argument。在第一个positinal argument后,剩下的参数都会被认为是positional,即使长得像参数。您可以参考例子来了解其作用。

该方法返回包含用户输入参数的字典,命令及选项是字典的键,相对应的用户输入是相应的值。

Help Message格式

Help Message具有分成两部分:

UsageOptionsUsage

Usage字段以 Usage: 开头,以空行结尾,所以最简单的例子就是这样子的:

"""Usage: my_program.py"""

Usage:后面的第一个字符会被替换成程序的名字。程序名后面接着就是参数等内容了,这又分为三种:

参数: 或者 ARGUMENT。注意,ARGUMENT是大写。选项: –options 或者 -o. 选项需在Options字段中进行介绍。选项支持合并,例如-iov会被分别解释为-o,-i,-v.选项的参数可以是--f=FILE,-f FILE,-fFILE命令

每个参数选项有以下格式:

[]:可选的元素,例如 my_program.py [-hvqo FILE]():必要参数,所有不在[]里头的都是必要参数,所以 my_program.py --path=<path> <file> == my_program.py (--path=<path> <file>)|: 或操作,()包含的参数必选其一.例如 (--clockwise|--counterwise),其中一个参数是必要的。[]包含的参数只选其一且不是必要的.[--left|--right].…: 表示还有更多参数。其返回的是一个数组.Options

一般来说,options的内容包括:

同义的选项,例如-v--verbose有参数的选项

Options字段的要求:

字符串中以---开始的行被解释为options的描述

Options:  --verbose   # GOOD  -o FILE     # GOODOther: --bad  # BAD, line does not start with dash "-"

对于有参数的字段,可以像下面展示的那样,以空格或=添加参数,相同意义的选项可以以空格或,区分

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

在参数的解释之前添加2个空格(这里一定要注意,不然会出问题)

--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

对于需要默认参数的,可以添加[default: <my-default-value>]:

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

详细例子

docopt官方提供了很多样例以供学习参考,非常方便。

类似git支持子命令

为了支持子命令,必须要将 options_first设置为True。docopt提供了一个演示例子,创建了类似于git的例子,具体请看该链接。

其他语言支持

docopt本身是一个python的库,不过有人已经移植到了其他语言中:

Ruby portCoffeeScript portLua portPHP port

相关文章:

你感兴趣的文章:

标签云: