72

有没有办法让 argparse 将两个引号之间的任何内容识别为单个参数?似乎一直看到破折号并假设它是一个新选项的开始

我有类似的东西:

mainparser = argparse.ArgumentParser()
subparsers = mainparser.add_subparsers(dest='subcommand')
parser = subparsers.add_parser('queue')
parser.add_argument('-env', '--extraEnvVars', type=str,
                        help='String of extra arguments to be passed to model.')
...other arguments added to parser...

但是当我运行时:

python Application.py queue -env "-s WHATEVER -e COOL STUFF"

它给了我:

Application.py queue: error: argument -env/--extraEnvVars: expected one argument

如果我不使用第一个破折号,它完全可以正常工作,但是我能够传入一个带有破折号的字符串是至关重要的。我试过用 \ 转义它,这会导致它成功,但会将 \ 添加到参数字符串中有人知道如何解决这个问题吗?无论 -s 是否是解析器中的参数,都会发生这种情况。

编辑:我正在使用 Python 2.7。

编辑2:

python Application.py -env " -env"

工作得很好,但是

python Application.py -env "-env"

才不是。

EDIT3:看起来这实际上是一个已经在争论的错误:http ://www.gossamer-threads.com/lists/python/bugs/89529,http : //python.6.x6.nabble.com/issue9334- argparse-does-not-accept-options-take-arguments-beginning-with-dash-regression-from-optp-td578790.html。它仅在 2.7 中而不在 optparse 中。

EDIT4:当前打开的错误报告是:http ://bugs.python.org/issue9334

4

7 回答 7

80

更新的答案:

调用时可以加上等号:

python Application.py -env="-env"

原答案:

我在做你想做的事情时也遇到了麻烦,但是 argparse 中有一个解决方法,它是parse_known_args方法。这将使您尚未定义的所有参数都通过解析器,并假设您会将它们用于子进程。缺点是您不会收到带有错误参数的错误报告,并且您必须确保您的选项和子流程的选项之间没有冲突。

另一种选择可能是强制用户使用加号而不是减号:

python Application.py -e "+s WHATEVER +e COOL STUFF"

然后在传递给子进程之前,在后处理中将“+”更改为“-”。

于 2013-04-23T17:02:15.870 回答
25

这个问题在http://bugs.python.org/issue9334中有深入讨论。大部分活动是在 2011 年。我去年添加了一个补丁,但有很多argparse补丁积压。

问题是字符串中的潜在歧义'--env',或者"-s WHATEVER -e COOL STUFF"当它跟随一个带有参数的选项时。

optparse做一个简单的从左到右的解析。第一个--env是带有一个参数的选项标志,因此它使用下一个参数,无论它看起来如何。 argparse,另一方面,循环遍历字符串两次。首先,它将它们分类为“O”或“A”(选项标志或参数)。在第二个循环中,它使用它们,使用re类似的模式匹配来处理变量nargs值。在这种情况下,看起来我们有OO, 两个标志并且没有参数。

使用时的解决方案argparse是确保参数字符串不会与选项标志混淆。此处(以及错误问题中)显示的可能性包括:

--env="--env"  # clearly defines the argument.

--env " --env"  # other non - character
--env "--env "  # space after

--env "--env one two"  # but not '--env "-env one two"'

其本身'--env'看起来像一个标志(即使被引用,请参阅sys.argv),但当后面跟着其他字符串时它不是。但是"-env one two"有问题,因为它可以被解析为['-e','nv one two']`'-e' 标志后跟一个字符串(甚至更多选项)。

--nargs=argparse.PARSER可用于强制argparse将所有后续字符串视为参数。但它们只在参数列表的末尾起作用。

在 issue9334 中有一个建议的补丁来添加args_default_to_positional=True模式。在这种模式下,解析器只将字符串分类为选项标志,如果它可以清楚地将它们与定义的参数匹配。因此 '--env --one' 中的 '--one' 将被归类为参数。但是 '--env --env' 中的第二个 '--env' 仍将被归类为选项标志。


展开相关案例

将 argparse 与以破折号 ("-") 开头的参数值一起使用

parser = argparse.ArgumentParser(prog="PROG")
parser.add_argument("-f", "--force", default=False, action="store_true")
parser.add_argument("-e", "--extra")
args = parser.parse_args()
print(args)

生产

1513:~/mypy$ python3 stack16174992.py --extra "--foo one"
Namespace(extra='--foo one', force=False)
1513:~/mypy$ python3 stack16174992.py --extra "-foo one"
usage: PROG [-h] [-f] [-e EXTRA]
PROG: error: argument -e/--extra: expected one argument
1513:~/mypy$ python3 stack16174992.py --extra "-bar one"
Namespace(extra='-bar one', force=False)
1514:~/mypy$ python3 stack16174992.py -fe one
Namespace(extra='one', force=True)

“-foo one”案例失败,因为-foo被解释为-f标志加上未指定的额外内容。这与允许-fe被解释为的动作相同['-f','-e']

如果我将 更改nargsREMAINDER(not PARSER),之后的所有内容都-e被解释为该标志的参数:

parser.add_argument("-e", "--extra", nargs=argparse.REMAINDER)

所有案例都有效。请注意,该值是一个列表。并且不需要引号:

1518:~/mypy$ python3 stack16174992.py --extra "--foo one"
Namespace(extra=['--foo one'], force=False)
1519:~/mypy$ python3 stack16174992.py --extra "-foo one"
Namespace(extra=['-foo one'], force=False)
1519:~/mypy$ python3 stack16174992.py --extra "-bar one"
Namespace(extra=['-bar one'], force=False)
1519:~/mypy$ python3 stack16174992.py -fe one
Namespace(extra=['one'], force=True)
1520:~/mypy$ python3 stack16174992.py --extra --foo one
Namespace(extra=['--foo', 'one'], force=False)
1521:~/mypy$ python3 stack16174992.py --extra -foo one
Namespace(extra=['-foo', 'one'], force=False)

argparse.REMAINDER就像'*',除了它接受后面的所有内容,无论它是否看起来像一个标志。 argparse.PARSER更像是“+”,因为它首先需要一个positional类似的参数。它nargssubparsers使用的。

记录了这种用法REMAINDERhttps://docs.python.org/3/library/argparse.html#nargs

于 2014-02-19T22:54:18.763 回答
19

python tst.py -e ' -e blah'作为一个非常简单的解决方法,您可以使用空格开始参数。如果您愿意,只需lstrip()选择将其恢复正常即可。

或者,如果第一个“子参数”不是原始函数的有效参数,那么您根本不需要做任何事情。也就是说,python tst.py -e '-s hi -e blah'不起作用的唯一原因是因为-stst.py.

此外,现在已弃用的optparse模块可以正常工作。

于 2013-04-23T17:13:31.343 回答
1

我已经将一个脚本从 optparse 移植到 argparse,其中某些参数的值可能以负数开头。我遇到了这个问题,因为该脚本在许多地方都使用了,而没有使用“=”符号将负值连接到标志。在阅读了此处和http://bugs.python.org/issue9334中的讨论后,我知道参数只取一个值,并且接受后续参数(即缺失值)作为值没有风险。FWIW,我的解决方案是在传递给 parse_args() 之前预处理参数并使用 '=' 加入有问题的参数:

def preprocess_negative_args(argv, flags=None):
    if flags is None:
        flags = ['--time', '--mtime']
    result = []
    i = 0
    while i < len(argv):
        arg = argv[i]
        if arg in flags and i+1 < len(argv) and argv[i+1].startswith('-'):
            arg = arg + "=" + argv[i+1]
            i += 1
        result.append(arg)
        i += 1
    return result

这种方法至少不需要任何用户更改,它只修改明确需要允许负值的参数。

>>> import argparse
>>> parser = argparse.ArgumentParser("prog")
>>> parser.add_argument("--time")
>>> parser.parse_args(preprocess_negative_args("--time -1d,2".split()))
Namespace(time='-1d,2')

告诉 argparse 哪些参数应该明确允许带有前导破折号的值会更方便,但这种方法似乎是一个合理的折衷方案。

于 2020-10-15T20:22:28.293 回答
1

类似的问题。我通过用“\”替换空格来解决这个问题。例如:

替换
python Application.py "cmd -option"

python Application.py "cmd\ -option"
不确定你的问题。

于 2021-11-12T01:58:41.767 回答
0

为了绕过必须处理 argparse 甚至查看不是您想要的标志的“-”,您可以在 argparse 读取它之前编辑 sys.argv。只需保存您不想看到的参数,在其位置放置一个填充参数,然后在 argparse 处理 sys.argv 后将填充参数替换为原始参数。我只需要为我自己的代码执行此操作。它不漂亮,但它有效并且很容易。如果您的标志并不总是以相同的顺序,您也可以使用 for 循环遍历 sys.argv。

parser.add_argument('-n', '--input', nargs='*')
spot_saver = ''
if sys.argv[1] == '-n':             #'-n' can be any flag you use
    if sys.argv[2][0] == '-':       #This checks the first character of the element
        spot_saver = sys.argv[2] 
        sys.argv[2] = "fillerText" 
args = parser.parse_args()
if args.input[0] == 'fillerText':
    args.input[0] = spot_saver
于 2020-11-02T06:39:04.657 回答
0
paser.add_argument("--argument_name", default=None, nargs=argparse.REMAINDER)

python_file.py --argument_name "--abc=10 -a=1 -b=2 cdef"

注意:参数值只能在双引号内传递,这不适用于单引号

于 2019-03-01T12:44:13.710 回答