Argparse 不会识别参数

Argparse will not recognize arguments

此脚本将打印环境变量。

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

之后分配的任何其他短标志

parser.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 环境”忽略但传递给 eval,就像使用 cat test.py 一样。我试过使用 sub_parser 但无济于事。出现相同的结果。

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

由于参数数量可变的选项几乎必须是 last 选项,因此请考虑使 --eval 成为一个标志,用于告诉您的编写如何解释其余 positonal 参数的程序。那么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" 这样的命令行。)

来自Argparse documentation

If you have positional arguments that must begin with - and don’t look like negative numbers, you can insert the pseudo-argument '--' which tells parse_args() that everything after that is a positional argument [...]

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

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