【问题标题】:Argparse will not recognize argumentsArgparse 将无法识别参数
【发布时间】:2021-08-16 18:30:27
【问题描述】:

此脚本将打印环境变量。

使用 Python 3.9。 目标是能够在需要时运行任何子命令。我得到的错误是,如果添加了任何额外的短标志,“忽略环境”arg 正在尝试解析它。我不想要这个。在--eval 之后分配的任何内容都会附加短标志。

解析器.py

import argparse, os 

def parseargs(p):
    p.usage = '%(prog)s [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]'
    p.add_argument(
        "-i",
        "--ignore-environment",
        action="store_const",
        const=dict(),
        dest="env",
        help="start with an empty environment",
        default=os.environ,
    )
    p.add_argument(
        "--export",
        nargs=1,
        help="Set argument with --export NAME=VALUE"
    )
    p.add_argument(
        "--eval",
        nargs="+",
        help="Run any commands with newly updated environment, " 
            "--eval COMMAND ARGS"
    )
return p

执行如下

>>> p = argparse.ArgumentParser()
>>> parseargs(p) # assigns arguments to parser
>>> p.parse_args('--export FOO=bar --eval cat test.py'.split()) # This is ok and works correctly. cat is the bash command
Namespace([os.environs..], eval=['cat', 'test.py'], export=['FOO=bar']) 
>>>p.parse_args('--export FOO=bar --eval ls -l'.split()) # This is fails
error: unrecognized arguments: -l

如何让“-l”被“-i/ignore environment”忽略但传递给 eval,例如使用 cat test.py。我曾尝试使用 sub_parser 但无济于事。出现同样的结果。

【问题讨论】:

  • --eval ls \-l?
  • @9769953 shell 在脚本执行之前执行引号移除; argparse 仍将看到 --evalls-l
  • @chepner 这里没有外壳。但实际上,在尝试时,它被 argparse 解析为文字 ``。
  • 正确,因为反斜杠转义是特定于 shell 的功能。 argparse 没有这样的概念。

标签: python argparse


【解决方案1】:

问题在于parse_args 在考虑任何实际选项的语义之前,会尝试在词法上识别可能的选项。

由于带有可变数量参数的选项几乎必须是始终使用的 last 选项,因此请考虑将 --eval 设置为一个标志,用于告诉您的程序如何解释剩余的 位置参数。那么ls-l可以被--抵消,防止parse_args认为-l是一个未定义的选项。

p.add_argument(
    "--eval",
    action='store_true',
    help="Run any commands with newly updated environment, " 
)

# zero or more, so that you don't have to provide a dummy argument
# when the lack of --eval makes a command unnecessary.
# Wart: you can still use --eval without specifying any commands.
# I don't believe argparse alone is capable of handling this,
# at least not in a way that is simpler than just validating
# arguments after calling parse_args().
p.add_argument('cmd_and_args', nargs='*')

那么你的命令行可能看起来像

>>> p.parse_args('--export FOO=bar --eval -- ls -l'.split())

甚至

>>> p.parse_args('--eval --export FOO=bar -- ls -l'.split())

稍后,您将使用 args.eval 的布尔值来决定如何处理列表 args.cmd_and_args

重要提示:其中一个问题是,您将这些选项附加到任意预先存在的解析器,这些解析器可能定义了自己的位置参数,所以让它与原始解析器配合得很好可能很困难,如果不是不可能的话。


另一种选择是采用单个参数进行内部解析。

p.add_arguments("--eval")

...

args = p.parse_args()
cmd_and_args = shlex.split(args.eval)  # or similar

然后

>>> p.parse_args(['--export', 'FOO=bar', '--eval', 'ls -l'])

(请注意,使用str.split 不适用于像--export FOO=bar --eval "ls -l" 这样的命令行。)

【讨论】:

  • 我不知道--。谢谢!!
【解决方案2】:

来自Argparse documentation

如果您的位置参数必须以 - 开头并且看起来不像负数,则可以插入伪参数“--”,它告诉 parse_args() 之后的所有内容都是位置参数 [... ]

因此,在您的情况下,您无法对添加或定义参数的方式进行任何更改,但是您提供的要解析的字符串应该在 eval 选项的参数之前有 --,如下所示:

--export FOO=bar --eval ls -- -l

【讨论】:

  • 但是-l 是一个位置参数,而不是--eval 选项的参数。您必须大幅调整解析器。
猜你喜欢
  • 2019-07-14
  • 2013-06-11
  • 1970-01-01
  • 2012-09-30
  • 2015-07-20
  • 1970-01-01
  • 2015-12-07
相关资源
最近更新 更多