Bash 4.2.46 getopts concatenated short options 并在处理过程中重置 OPTIND 导致无限循环:如何在 Bash 中解决这个问题?

Bash 4.2.46 getopts concatenated short options and resetting OPTIND during processing leads to an infinite loop: How can this be remedied in Bash?

我在最新的 CentOS 7 VM 中使用以下 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 赋值。如果将其删除,则不会发生该行为。感觉好像有一些隐藏的子字符串索引正在进行:

也许这些是由手册页中未描述的其他 getopts 参数指示的。在编写包装器以允许部分 argument/nested getopts 处理时,任何人都可以阐明这一点,这可能会帮助我解决这个问题吗?


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

How can this be remedied in Bash?

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

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

或更简单 - 我看到 getopts.def 可加载内置程序,您可以修补它以向 serialize/deserialize getopts 状态添加一些附加选项。

您还可以提供自己的 getopts 实现作为 bash 函数,具有自定义语义和自定义状态 serializer/deserializer。

Can anyone shed any light on

来自 posix getopts:

If the application sets OPTIND to the value 1, a new set of parameters can be used: either the current positional parameters or new arg values. Any other attempt to invoke getopts multiple times in a single shell execution environment with parameters (positional parameters or arg operands) that are not the same in all invocations, or with an OPTIND value modified to be a value other than 1, produces unspecified results.

据我们所知:

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

resolve this one issue when writing my wrapper to allow for partial argument/nested getopts handling?

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

不要嵌套getopts,它不可重入,无法影响它的内部状态,它使用全局变量。 getopts sets OPTIND,不需要读,除非OPTIND重新设置为1,在哪种情况 getopts 被重置。任何其他值都可能被忽略。一个接一个地叫getopts就可以了。