最后一个位置参数后不要解析选项
我正在为 ssh
命令行客户端写一个封装器。在第一个属于 command
的位置参数之后,所有后续的选项也应该被当作位置参数来处理。
在 optparse
中,我认为可以通过 disable_interspersed_args
来实现这个功能。
目前我有这样的代码:
parser = argparse.ArgumentParser()
parser.add_argument('--parallel', default=False, action='store_true')
# maybe allow no command? this would ssh interactively into each machine...
parser.add_argument('command', nargs='+')
args = parser.parse_args()
但是如果选项作为命令的一部分传递(比如 my_wrapper ls -l
),它们会被 ArgumentParser
解释为未知选项,出现 error: unrecognized arguments: -l
的错误。
如果我使用 parse_known_args()
,选项的顺序可能会被打乱。
p = argparse.ArgumentParser()
p.add_argument('-a', action='store_true')
p.add_argument('command', nargs='+')
print(p.parse_known_args())
$ python3 bah.py -b ls -l -a
(Namespace(a=True, command=['ls']), ['-b', '-l'])
在这里你可以看到,-b
在 ls
前面的位置信息丢失了,而 -a
也被从命令中解析出来,这并不是我想要的结果。
我该如何:
- 在某个特定点之后阻止参数被解析?
- 禁用交错参数的解析?
- 允许带前缀的参数被当作位置参数处理?
4 个回答
0
@dcolish 提出的建议是一个通用的方法。这里有一个示例实现,它还支持标准的 --
分隔符,但使用这个分隔符并不是正确解析的必要条件。
结果:
# ./parse-pos.py -h
usage: parse-pos.py [-h] [-qa] [-qb] COMMAND [ARGS...]
# ./parse-pos.py -qa ls -q -h aa /bb
try_argv = ['-qa', 'ls']
cmd_rest_argv = ['-q', '-h', 'aa', '/bb']
parsed_args = Namespace(command='ls', qa=True, qb=False)
代码:
#!/usr/bin/python3
import argparse
import sys
from pprint import pprint
class CustomParserError(Exception):
pass
class CustomArgumentParser(argparse.ArgumentParser):
def error(self, message):
raise CustomParserError(message)
def original_error(self, message):
super().error(message)
def parse_argv():
parser = CustomArgumentParser(description='Example')
parser.add_argument('command', metavar='COMMAND [ARGS...]', help='the command to be executed')
parser.add_argument('-qa', action='store_true') # "ambiguous option" if you specify just "-q"
parser.add_argument('-qb', action='store_true') # "ambiguous option" if you specify just "-q"
def parse_until_positional(parser, _sys_argv = None):
if _sys_argv is None:
_sys_argv = sys.argv[1:] # skip the program name
for i in range(0, len(_sys_argv) + 1):
try_argv = _sys_argv[0:i]
try:
parsed_args = parser.parse_args(try_argv)
except CustomParserError as ex:
if len(try_argv) == len(_sys_argv):
# this is our last try and we still couldn't parse anything
parser.original_error(str(ex)) # sys.exit()
continue
# if we are here, we parsed our known optional & dash-prefixed parameters and the COMMAND
cmd_rest_argv = _sys_argv[i:]
break
return (parsed_args, cmd_rest_argv, try_argv)
(parsed_args, cmd_rest_argv, try_argv) = parse_until_positional(parser)
# debug
pprint(try_argv)
pprint(cmd_rest_argv)
pprint(parsed_args)
return (parsed_args, cmd_rest_argv)
def main():
parse_argv()
main()
5
我觉得你解决这些问题的最好办法是,在所有可选参数后面加上 --
。这个 --
是一个伪参数,它告诉 ArgumentParser 后面的内容都是位置参数。相关文档可以在 这里 找到。
至于如何防止在某个点之后解析参数,你可以把部分 argv 传给 parse_args。结合一些自省(也就是查看对象的属性和方法)可以用来限制解析的内容。
21
我也遇到过同样的问题。我在argparse的错误追踪器上找到了解决办法:http://code.google.com/p/argparse/issues/detail?id=52
解决方法很简单:把 nargs='+'
(或者 '*'
)替换成 nargs=argparse.REMAINDER
。这个特殊的值虽然没有文档说明,但它能实现你想要的效果。