不要解析最后一个位置参数之后的选项

发布于 2024-11-17 10:18:40 字数 1283 浏览 4 评论 0原文

我正在围绕 ssh 命令行客户端编写一个包装器。在作为命令一部分的第一个位置参数之后,所有其他选项也应被视为位置参数。

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 解释为未知选项。 错误:无法识别的参数:-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'])

这里可以看到-bls之前的位置已经丢失,并且-a已经从命令中解析出来了,即不想要的。

我怎样才能:

  • 防止参数在某一点之后被解析?
  • 禁用散布参数的解析?
  • 允许带有前缀的参数作为位置参数使用吗?

I'm writing a wrapper around the ssh command line client. After the first positional argument that's part of command, all further options should also be treated as positional arguments.

Under optparse, I believe this would be done with disable_interspersed_args.

Presently I have something like this:

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()

But if options are passed as part of the command (such as my_wrapper ls -l), they're instead interpreted by ArgumentParser as unknown options. error: unrecognized arguments: -l

If I use parse_known_args(), the options may be taken out of order.

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'])

Here you can see that -b's position before ls has been lost, and -a has been parsed out from the command, which is not desired.

How can I:

  • Prevent arguments from being parsed after a certain point?
  • Disable parsing of interspersed arguments?
  • Allow arguments with a prefix to be consumed as positional arguments?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

热血少△年 2024-11-24 10:18:40

我也有同样的问题。我在 argparse bug 跟踪器上找到了解决方案:http://code.google .com/p/argparse/issues/detail?id=52

解决方案很简单:将 nargs='+' (或 '*')替换为nargs=argparse.REMAINDER。这个特殊值没有记录,但它可以满足您的需求。

I had the same problem. I found the solution on the argparse bug tracker: http://code.google.com/p/argparse/issues/detail?id=52

The solution is simple: replace nargs='+' (or '*') with nargs=argparse.REMAINDER. This special value is not documented, but it does what you want.

如梦 2024-11-24 10:18:40

我认为开始解决这些问题的最佳选择是在所有可选参数之后尝试 ---- 是一个伪参数,它告诉 ArgumentParser 之后的所有内容都是位置参数。文档位于此处

至于防止参数在特定时间后被解析一点,您可以将 argv 的一部分传递给 parse_args。与一些内省相结合可以用来限制解析的内容。

I think your best bet to start solving these issues is to try out -- after all your optional args. -- is a pseudo-arg that tells ArgumentParser that everything after is a positional argument. Docs are here

As for prevent arguments from being parsed after a certain point, you can pass part of argv to parse_args. That combined with some introspection can be used to limit what is parsed.

你没皮卡萌 2024-11-24 10:18:40

@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()

What @dcolish suggested is the universal approach. Here is a sample implementation which also supports the standard -- separator, but its usage is not required for correct parsing.

Result:

# ./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)

The code:

#!/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()
草莓酥 2024-11-24 10:18:40

另一种选择是使用 parse_known_args,它会在以下情况下停止解析:遇到未知参数。

Another option is to use parse_known_args, which stops parsing when an unknown argument is encountered.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文