问题描述
我在最新的 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:2:1}
代表 y
${1:1:1}
也许这些是由手册页中未描述的其他 getopts 参数指示的。在编写包装器以允许部分参数/嵌套 getopts 处理时,任何人都可以对此有所了解以帮助我解决这个问题吗?
好奇:我已经为嵌套的 getopts 处理实现了一些包装函数。这些允许对参数进行部分处理,在此期间可能会调用也使用包装函数进行 getopts 处理的函数。我将 OPTIND
值保存在堆栈数组变量中,并且当从嵌套中弹出时,需要重置 OPTIND
。除了使用连接的短标志参数的情况外,这一切都很好。 (该实现还使指定长选项成为可能。)
解决方法
如何在 Bash 中解决这个问题?
好吧,通过修补源。但我不想这样做,我相信目前的行为是正确的。
您可以向 getopt.c
添加一个附加函数,并通过 variables.c
中的一些特殊变量将其公开,从而允许操作 getopts
内部状态。
或者更简单 - 我看到 getopts.def 可加载内置函数,您可以修补它以添加一些附加选项来序列化/反序列化 getopts 状态。
您还可以将自己的 getopts
实现作为具有自定义语义和自定义状态序列化器/反序列化器的 bash 函数提供。
谁能解释一下
如果应用程序将 OPTIND 设置为值 1,则可以使用一组新参数:当前位置参数或新的 arg 值。任何在单个 shell 执行环境中多次调用 getopts 的尝试,其参数(位置参数或 arg 操作数)在所有调用中都不相同,或者 OPTIND 值修改为 1 以外的值,都会产生未指定的结果。
据我们所知:
- setting OPTIND=1 resets the internal state of getopt ()
-
未指定如果修改
OPTIND
会发生什么。
您所看到的行为已记录在案 - 将 OPTIND=1
设置为 $OPTIND
等于 1
重置 getopt()
,这会导致无限循环,正如人们所期望的那样。除此之外,bash 文档没有指定修改 OPTIND
时会发生什么。不要做。您认为将 OPTIND
设置为自定义值会以特定方式影响 getopts
的期望没有任何依据。不会。
在编写我的包装器以允许部分参数/嵌套 getopts 处理时解决这个问题吗?
如果您正在编写自己的参数解析模块,请不要使用 getopts
并且不要依赖于未定义、未指定或实现定义的行为。我建议以与 GNU getopt
相同的方式来做 - 在单独的子进程中生成一个 shell 可源字符串,而不是依赖全局变量 OPT*
并贡献意大利面条代码。
不要嵌套 getopts
,它不是可重入的,并且无法影响它的内部状态,它使用全局变量。 getopts
sets OPTIND
,不需要读取,除非OPTIND
重置为1
,在这种情况下{ {1}} 被重置。可以忽略任何其他值。只需一个接一个地调用 getopts
。