实现“[命令] [操作] [参数]”风格的命令行界面?

16

实现一个类似于git的命令行UI,哪种方式最为简洁?

git push origin/master
git remote add origin git://example.com master

理想情况下还应允许更灵活的解析,例如:

jump_to_folder app theappname v2
jump_to_folder app theappname source
jump_to_folder app theappname source v2
jump_to_folder app theappname build v1
jump_to_folder app theappname build 1
jump_to_folder app theappname v2 build

jump_to_folder是脚本的名称,app是命令,theappname是一个“固定位置”的参数,“build”和“v2”等是参数(例如,可能的参数是任何数字/带有v前缀的任何数字,或者build/source/tmp/config)。

我可以通过一系列的if/else/elifs手动解析这些参数,但肯定有一种更优雅的方法来做到这一点?

完全理论上来说,我可以描述UI模式...

app:
    fixed: application_name

    optional params:
        arg subsection:
            "build"
            "source"
            "tmp"
            "config"

        arg version:
            integer
            "v" + integer

然后通过上述模式解析提供的参数,得到一个字典:
>>> print schema.parse(["app", "theappname", "v1", "source"])
{
    "application_name": "theappname",
    "params":{
        "subsection": "source",
        "version":"v1"
    }
}

这样的系统是否存在?如果不存在,我应该如何实施类似的东西?

6个回答

17

argparse非常适合这种情况,特别是"子命令"和位置参数。

import argparse


def main():
    arger = argparse.ArgumentParser()

    # Arguments for top-level, e.g "subcmds.py -v"
    arger.add_argument("-v", "--verbose", action="count", default=0)

    subparsers = arger.add_subparsers(dest="command")

    # Make parser for "subcmds.py info ..."
    info_parser = subparsers.add_parser("info")
    info_parser.add_argument("-m", "--moo", dest="moo")

    # Make parser for "subcmds.py create ..."
    create_parser = subparsers.add_parser("create")
    create_parser.add_argument("name")
    create_parser.add_argument("additional", nargs="*")

    # Parse
    opts = arger.parse_args()

    # Print option object for debug
    print opts

    if opts.command == "info":
        print "Info command"
        print "--moo was %s" % opts.moo

    elif opts.command == "create":
        print "Creating %s" % opts.name
        print "Additional: %s" % opts.additional

    else:
        # argparse will error on unexpected commands, but
        # in case we mistype one of the elif statements...
        raise ValueError("Unhandled command %s" % opts.command)


if __name__ == '__main__':
    main()
这可以这样使用:
$ python subcmds.py create myapp v1 blah
Namespace(additional=['v1', 'blah'], command='create', name='myapp', verbose=0)
Creating myapp
Additional: ['v1', 'blah']
$ python subcmds.py info --moo
usage: subcmds.py info [-h] [-m MOO]
subcmds.py info: error: argument -m/--moo: expected one argument
$ python subcmds.py info --moo 1
Namespace(command='info', moo='1', verbose=0)
Info command
--moo was 1

1
太好了!这正是医生所开的药方!谢谢! - Eugene Sajine

9
cmd 模块可能非常适合这个任务。
示例:
import cmd

class Calc(cmd.Cmd):
    def do_add(self, arg):
        print sum(map(int, arg.split()))

if __name__ == '__main__':
    Calc().cmdloop()

运行它:

$python calc.py
(Cmd) add 4 5
9
(Cmd) help

Undocumented commands:
======================
add  help

(Cmd)

查看Python文档PyMOTW网站了解更多信息。


2
直接从我的脚本中提取出来:
import sys

def prog1_func1_act1(): print "pfa1"
def prog2_func2_act2(): print "pfa2"

commands = {
    "prog1 func1 act1": prog1_func1_act1,
    "prog2 func2 act2": prog2_func2_act2
}

try:
    commands[" ".join(sys.argv[1:])]()
except KeyError:
    print "Usage: ", commands.keys()

这是一种相当快速和简单的解决方案,但非常适合我的使用。如果我稍微整理一下,可能会将argparse添加到混合中以解析位置参数和关键字参数。


问题在于参数是固定的。例如,除非为每个组合创建一个键,否则您无法轻松地拥有“v001”..“v102”选项。 - dbr
是的,我知道,这就是为什么我提到了argparse。例如,如果您有相对较少的“命令”和“操作”,但有很多参数,您可以使用我使用的方法分派到特定的操作,然后将参数(和可选关键字参数)传递给使用argparse的脚本。 - klozovin
此外,您可以添加一个元命令“help”,它会打印出命令的内容并显示它们的.__doc__字符串。 - pjz
另外,args并不是固定的,因为命令列表中的所有命令都使用所有的args,因此它们可以在每个命令中进行解析(常见的解析可以被分解出来)。或者,如果您的意思是命令列表是有限的,请考虑在命令对象中覆盖__getkey__。 - pjz

1

Python有一个用于解析命令行选项的模块,optparse


很遗憾,它解析的是命令行选项,而不是dbr所要求的参数 - Piotr Lesnicki
没错。就我所知,它最适合“-f 2 -v -z 55”这样的参数,除非我漏掉了什么? - dbr

1

0

这是我的建议。

  1. 稍微修改一下你的语法。

  2. 使用optparse。

最好还能允许更灵活的解析,例如:

jump_to_folder -n theappname -v2 cmd 
jump_to_folder -n theappname cmd source 
jump_to_folder -n theappname -v2 cmd source 
jump_to_folder -n theappname -v1 cmd build 
jump_to_folder -n theappname -1 cmd build 
jump_to_folder -n theappname -v2 cmd build

如果你有1或2个参数:命令始终是第一个参数。它的可选参数始终是第二个参数。

其他所有内容都是选项,没有特定顺序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接