argparse 仅在位置参数之前解析

Posted

技术标签:

【中文标题】argparse 仅在位置参数之前解析【英文标题】:Argparse parse only before positional argument 【发布时间】:2018-12-22 12:08:27 【问题描述】:

如何让 argparse 只解析位置参数之后的命令?

如果我有命令 pythonfile.py -d dir -e test pos_cmd_1 -d

我怎样才能让第一个 -d 由 argparse 解析,而位置命令之后的任何内容都由该命令本身解析(基本上将 pos_cmd_1 -d 作为单个参数读取)

所以参数列表将是

pythonfile.py -d 目录 -e 测试 pos_cmd_1 -d -s -etc

因此,位置命令之前的任何内容都是可选的。位置命令之后的任何内容都将成为位置命令本身的一部分。

编辑:当尝试使用双破折号运行命令时,它告诉我后面的参数无法识别。

pythonfile.py -d testdir -e test -- command -d -s

它说-d -s 是无法识别的参数,而不是将它们与命令捆绑在一起。

【问题讨论】:

用双破折号结束开关怎么样? pythonfile.py -d dir -e test -- pos_cmd_1 -d 至少很清楚选项在哪里停止 您能否提供一些背景信息:您认为您为什么要这样做? @jonrsharpe 为 docker-compose 编写一个包装器,让我可以添加自定义可扩展性,并且一些参数标志与 docker 命令的参数标志重叠。因此,如果 -d 在 compose 命令(up/start/kill/etc)之前,则将其解析为我的命令,但如果在之后,它将成为 docker 命令的一部分。所以python.py -d test up -d,其中第二个-d 将由docker 解析。 在这种情况下使用--,它经常出现在“...然后将这些参数传递给任何那个调用”。 在我看来,应该从 shell 正确调用该实用程序(即根据其记录的用法)。为此目的使用引号或双破折号。这些都是众所周知的标准,应该遵守。示例:pythonfile.py -d dir -e test "pos_cmd_1 -d" 【参考方案1】:

你可以通过稍微改变你的命令行来实现这一点

pythonfile.py -d dir -e test -- pos_cmd_1 -d

通过添加--,您告诉 argparse 停止寻找选项。所以所有剩余的参数都设置在位置参数列表中。

另一种方法是引用其余参数:

pythonfile.py -d dir -e test "pos_cmd_1 -d"

并且(因为它只创建一个位置参数)在拆分后的字符串上再次使用参数解析器(如果您想在这些参数中传递带引号的字符串,则不理想)

这些方法的优点是它们受到argparsegetopt 的原生支持,而且这是一种标准机制,不会让您的命令的用户感到惊讶。

如果你想坚持你的方法,也许你可以预处理参数列表,通过连续检测 2 个非选项参数来插入双破折号:

args = "-d dir -e test pos_cmd_1 -d".split()
oldarg=""
for i,a in enumerate(args):
    if oldarg and oldarg[0]!='-' and a[0]!='-':
        args.insert(i,'--')
        break
    oldarg = a

args 现在是:['-d', 'dir', '-e', 'test', '--', 'pos_cmd_1', '-d']

【讨论】:

这两种方法都有效,但是否没有选项可以让我在无需用户自己输入 -- 的情况下执行此操作?我唯一的选择是在位置命令之后预解析字符串以获取所有内容吗?位置命令可能有很多东西,所以我只需要通过将 arg 列表中的每个项目与可能的命令进行比较,将它和它之后的所有内容放入自己的字符串中吗?【参考方案2】:

使用简单的解析器:

In [2]: p = argparse.ArgumentParser()
In [3]: p.add_argument('-d');
In [4]: p.add_argument('-e');
In [5]: p.parse_args('-d dir -e test pos_cmd_1 -d'.split())
usage: ipython3 [-h] [-d D] [-e E]
ipython3: error: argument -d: expected one argument

它尝试解析最后一个'-d'并遇到错误。 parse_known_args 没有帮助。

使用 '-d' 和 '-e' 以外的字符串 parse_known_args 有效:

In [7]: p.parse_known_args('-d dir -e test pos_cmd_1 -s'.split())
Out[7]: (Namespace(d='dir', e='test'), ['pos_cmd_1', '-s'])

带有REMAINDER nargs 的位置似乎可以工作:

In [8]: a1 = p.add_argument('rest', nargs='...') # argparse.REMAINDER
In [9]: p.parse_args('-d dir -e test pos_cmd_1 -s'.split())
Out[9]: Namespace(d='dir', e='test', rest=['pos_cmd_1', '-s'])
In [10]: p.parse_args('-d dir -e test pos_cmd_1 -d'.split())
Out[10]: Namespace(d='dir', e='test', rest=['pos_cmd_1', '-d'])

REMAINDER 应该像“--”一样工作,捕获输入以供其他解析器或命令使用。

如果预计会捕获整个命令行,则可能会出现问题,如下所示:

In [12]: p.parse_args('-s pos_cmd_1 -d'.split())
usage: ipython3 [-h] [-d D] [-e E] ...
ipython3: error: unrecognized arguments: -s

https://docs.python.org/3/library/argparse.html#nargs

【讨论】:

以上是关于argparse 仅在位置参数之前解析的主要内容,如果未能解决你的问题,请参考以下文章

如何使 argparse 将所有参数视为位置?

如何创建具有多个位置参数的 argparse 互斥组?

python argparse详解

Python技法:用argparse模块解析命令行选项

解析多个位置参数的问题

Python 处理脚本的命令行参数:使用argparse