Bash 4.2.46 getopts 连接短选项并在处理期间重置 OPTIND 会导致无限循环:如何在 Bash 中解决此问题?

问题描述

我在最新的 CentOS 7 虚拟机中使用以下 Bash 版本:

GNU bash,version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

以下代码按预期执行(注意 -x -y):

set -- -x -y; OPTIND=1; while getopts xy opt; do echo $opt; OPTIND=$OPTIND; done
x
y

但是,当我将两个短选项组合到 -xy 时,会发生无限循环:

set -- -xy; OPTIND=1; while getopts xy opt; do echo $opt; OPTIND=$OPTIND; done
x
x
x ... infinite output

触发器是 OPTIND=$OPTIND 赋值。如果将其删除,则不会发生该行为。感觉像是在进行一些隐藏的子字符串索引

  • -xy 不仅仅是 $1 可以描述为单个整数的索引 OPTIND
  • 感觉就像 x
  • 的索引 ${1:1:1}
  • ${1:2:1} 代表 y

也许这些是由手册页中未描述的其他 getopts 参数指示的。在编写包装器以允许部分参数/嵌套 getopts 处理时,任何人都可以对此有所了解以帮助我解决这个问题吗?


好奇:我已经为嵌套的 getopts 处理实现了一些包装函数。这些允许对参数进行部分处理,在此期间可能会调用也使用包装函数进行 getopts 处理的函数。我将 OPTIND 值保存在堆栈数组变量中,并且当从嵌套中弹出时,需要重置 OPTIND。除了使用连接的短标志参数的情况外,这一切都很好。 (该实现还使指定长选项成为可能。)

解决方法

如何在 Bash 中解决这个问题?

好吧,通过修补源。但我不想这样做,我相信目前的行为是正确的。

您可以向 getopt.c 添加一个附加函数,并通过 variables.c 中的一些特殊变量将其公开,从而允许操作 getopts 内部状态。

或者更简单 - 我看到 getopts.def 可加载内置函数,您可以修补它以添加一些附加选项来序列化/反序列化 getopts 状态。

您还可以将自己的 getopts 实现作为具有自定义语义和自定义状态序列化器/反序列化器的 bash 函数提供。

谁能解释一下

来自posix getopts

如果应用程序将 OPTIND 设置为值 1,则可以使用一组新参数:当前位置参数或新的 arg 值。任何在单个 shell 执行环境中多次调用 getopts 的尝试,其参数(位置参数或 arg 操作数)在所有调用中都不相同,或者 OPTIND 值修改为 1 以外的值,都会产生未指定的结果。

据我们所知:

您所看到的行为已记录在案 - 将 OPTIND=1 设置为 $OPTIND 等于 1 重置 getopt(),这会导致无限循环,正如人们所期望的那样。除此之外,bash 文档没有指定修改 OPTIND 时会发生什么。不要做。您认为将 OPTIND 设置为自定义值会以特定方式影响 getopts 的期望没有任何依据。不会。

在编写我的包装器以允许部分参数/嵌套 getopts 处理时解决这个问题吗?

如果您正在编写自己的参数解析模块,请不要使用 getopts 并且不要依赖于未定义、未指定或实现定义的行为。我建议以与 GNU getopt 相同的方式来做 - 在单独的子进程中生成一个 shell 可源字符串,而不是依赖全局变量 OPT* 并贡献意大利面条代码。

不要嵌套 getopts,它不是可重入的,并且无法影响它的内部状态,它使用全局变量。 getopts sets OPTIND,不需要读取,除非OPTIND重置为1,在这种情况下{ {1}} 被重置。可以忽略任何其他值。只需一个接一个地调用 getopts