getopts遇到'--'时的行为探究:为何case的-)分支不匹配?
我来帮你理清getopts遇到'--'时的具体行为,这其实是POSIX标准里明确规定的逻辑,很多刚用getopts的开发者都会遇到这个疑问:
'--' 不是普通选项,也不会被
\?)分支匹配:getopts的\?)分支是用来匹配那些不在你定义的选项列表里的单个字符选项(比如你只定义了ab:,那-c就会触发这个分支),但'--'是两个连字符开头,完全不符合单个选项的格式,所以根本不会进入这个分支处理。'--' 是选项与非选项参数的分隔符,getopts遇到它会立即停止解析选项:这是核心规则,一旦getopts扫描到'--',它会立刻终止选项解析循环,后续的所有参数——哪怕是以'-'开头的——都会被当作非选项参数处理,不再尝试解析为选项。
给你举个实际的代码例子,一看就明白:
#!/bin/bash # 定义支持的选项:-a(无参数)、-b(带参数) while getopts "ab:" opt; do case $opt in a) echo "解析到选项:-a" ;; b) echo "解析到选项:-b,参数为 $OPTARG" ;; \?) echo "无效选项:-$OPTARG" >&2 ;; :) echo "选项 -$OPTARG 需要传入参数" >&2 ;; esac done # 移除已经解析过的选项部分,剩下的就是非选项参数 shift $((OPTIND-1)) echo "剩余的非选项参数:$@"
如果运行命令:./test.sh -a -- -b hello world,输出结果会是:
解析到选项:-a
剩余的非选项参数:-b hello world
你看,'--'后面的'-b'完全没有被当作选项解析,而是和hello、world一起变成了普通参数。
另外补充个细节:getopts不会把'--'当作非选项参数保留,当它遇到'--'后,OPTIND会自动指向'--'后面的第一个参数,所以通过shift $((OPTIND-1))就能把包括'--'在内的已解析部分全部移除,剩下的就是纯粹的非选项参数。
总结一下:
'--' 是一个特殊的分隔标记,既不属于普通选项,也不属于非选项参数,它的唯一作用就是告诉getopts:“到此为止,后面的都不用当选项解析了”。
内容的提问来源于stack exchange,提问作者Tim

