我试图找出如何使用argparser来执行以下操作:
$ python test.py executeBuild --name foobar1 executeBuild --name foobar2 ....
getBuild
本身就是一个子命令。我的目标是让脚本能够链接一系列子命令(executeBuild
是其中之一)并按顺序执行它们。在上面的示例中,它将执行构建,然后设置环境,然后再次执行构建。我怎样才能用argparse实现这个目标?我尝试过以下方法:
main_parser = argparse.ArgumentParser(description='main commands')
subparsers = main_parser.add_subparsers(help='SubCommands', dest='command')
build_parser = subparsers.add_parser('executeBuild')
build_parser.add_argument('--name', action='store', nargs=1, dest='build_name')
check_parser = subparsers.add_parser('setupEnv')
args, extra=main_parser.parse_known_args()
但是,似乎每当我这样做时,它会进入executeBuild
的子命令并报告它不知道executeBuild
是什么。我已经尝试解析额外的内容,所以我可以做一个重复的调用/链,但是,第一个视图属性似乎已被覆盖,所以我甚至不能保存额外的选项并通过迭代。 / p>
答案 0 :(得分:4)
你问的是argparse
它没有写的东西:它擅长解析一个命令行(但只有一个)并且你想要在一行中解析多个命令。恕我直言,你必须在你的参数数组上进行初始拆分,然后在每个子命令上使用argparse
。下面的函数获取一个参数列表(可能是sys.argv
),跳过第一个并从每个已知子命令开始的数组中拆分。然后,您可以在每个子列表中使用argparse:
def parse(args, subcommands):
cmds = []
cmd = None
for arg in args[1:]:
if arg in (subcommands):
if cmd is not None:
cmds.append(cmd)
cmd = [arg]
else:
cmd.append(arg)
cmds.append(cmd)
return cmds
在你的例子中:
parse(['test.py', 'executeBuild', '--name', 'foobar1', 'executeBuild', '--name', 'foobar2'],
('executeBuild',))
=>
[['executeBuild', '--name', 'foobar1'], ['executeBuild', '--name', 'foobar2']]
限制:子命令用作保留字,不能用作选项参数。
答案 1 :(得分:2)
预先分割sys.argv
是一个很好的解决方案。但是也可以在使用nargs=argparse.REMAINDER
的参数进行解析时完成。这种类型的参数获取其余的字符串,无论它们是否看起来像是标志。
使用以下代码替换parse_known_args
:
...
build_parser.add_argument('rest', nargs=argparse.REMAINDER)
check_parser.add_argument('rest', nargs=argparse.REMAINDER)
extras = 'executeBuild --name foobar1 setupEnv executeBuild --name foobar2'.split()
# or extras = sys.argv[1:]
while extras:
args = main_parser.parse_args(extras)
extras = args.rest
delattr(args,'rest')
print args
# collect args as needed
打印:
Namespace(build_name=['foobar1'], command='executeBuild')
Namespace(command='setupEnv')
Namespace(build_name=['foobar2'], command='executeBuild')
在文档中:
argparse.REMAINDER。所有剩余的命令行参数都收集到一个列表中。这对于调度到其他命令行实用程序的命令行实用程序通常很有用:
REMAINDER
的问题是太贪心了。 http://bugs.python.org/issue14174。因此,build_parser
和check_parser
无法拥有其他位置参数。
贪婪REMAINDER
的方法是使用argparse.PARSER
。这是nargs
使用的subparsers
值(未记录)。它与REMAINDER
类似,只是第一个字符串看起来像一个'参数' (不是' - '),并与choices
匹配(如果给定)。 PARSER
并不像REMAINDER
一样贪婪,所以子分析师可以有其他位置参数。
还有一些额外的代码涉及'退出'字符串和虚拟解析器。这是为了解决PARSER
论证是否需要' (有点像nargs='+'
)
from argparse import ArgumentParser, PARSER, SUPPRESS
main_parser = ArgumentParser(prog='MAIN')
parsers = {'exit': None}
main_parser.add_argument('rest',nargs=PARSER, choices=parsers)
build_parser = ArgumentParser(prog='BUILD')
parsers['executeBuild'] = build_parser
build_parser.add_argument('cmd')
build_parser.add_argument('--name', action='store', nargs=1, dest='build_name')
build_parser.add_argument('rest',nargs=PARSER, choices=parsers, help=SUPPRESS)
check_parser = ArgumentParser(prog='CHECK')
parsers['setupEnv'] = check_parser
check_parser.add_argument('cmd')
check_parser.add_argument('foo')
check_parser.add_argument('rest',nargs=PARSER, choices=parsers, help=SUPPRESS)
argv = sys.argv[1:]
if len(argv)==0:
argv = 'executeBuild --name foobar1 setupEnv foo executeBuild --name foobar2'.split()
argv.append('exit') # extra string to properly exit the loop
parser = main_parser
while parser:
args = parser.parse_args(argv)
argv = args.rest
delattr(args,'rest')
print(parser.prog, args)
parser = parsers.get(argv[0], None)
示例输出:
('MAIN', Namespace())
('BUILD', Namespace(build_name=['foobar1'], cmd='executeBuild'))
('CHECK', Namespace(cmd='setupEnv', foo='foo'))
('BUILD', Namespace(build_name=['foobar2'], cmd='executeBuild'))
另一种可能性是使用'--'
来分隔命令块:
'executeBuild --name foobar1 -- setupEnv -- executeBuild --name foobar2'
但是,如果有多个'--'
:http://bugs.python.org/issue13922