我在 Bash 中定义了该函数,f
基于这里的例子(在“带有参数的选项”下):
f () {
while getopts ":a:" opt; do
case $opt in
a)
echo "-a was triggered, Parameter: $OPTARG" >&2
;;
\?)
echo "Invalid option: -$OPTARG" >&2
return 1
;;
:)
echo "Option -$OPTARG requires an argument." >&2
return 1
;;
esac
done
}
他们使用脚本,而我直接在 shell 中定义函数。
当我第一次启动 Bash 并定义函数时,一切正常:f -a 123
prints -a was triggered, Parameter: 123
。但是当我第二次运行完全相同的线路时,没有打印任何内容。
是什么导致了这种行为?它发生在 Bash 3.2 和 4.3 中,但在 Zsh 5.1 中运行良好。这是令人惊讶的,因为该示例应该是针对 Bash 的,而不是针对 Zsh 的。
答案1
bash 获取选项使用环境变量OPTIND跟踪处理的最后一个选项参数。事实上,OPTIND
每次在同一个 shell 会话中调用时,不会自动重置getopts
,只有在调用 shell 时才会自动重置。因此,从您第二次getopts
在同一会话中使用相同的参数调用时,OPTIND
没有改变,getopts
认为它已经完成了工作并且什么也没做。
您可以OPTIND
手动重置以使其正常工作:
$ OPTIND=1
$ f -a 123
-a was triggered, Parameter: 123
或者只是将函数放入脚本中并多次调用脚本。
zsh 获取选项略有不同。OPTIND
通常每次退出 shell 函数时都会重置为 1。
答案2
在任何函数中声明局部变量都是一个神的习惯。如果您声明 $opt、$OPTARG 和 $OPTIND,则 getopts 将在您调用该函数时随时运行。函数完成后,局部变量将被丢弃。
#!/bin/bash
function some_func {
declare opt
declare OPTARG
declare OPTIND
while getopts ":a:" opt; do
echo $opt is $OPTARG
done
}
答案3
您需要OPTIND=1
在函数开始时设置f
。默认情况下它是 1,但随着参数的解析而增加。当您getopts
再次调用时,它会从中断处继续。如果您的第二次调用是:
f -a 123 -a 999
当它打印 999 时。
答案4
当getopt
被调用时,它会通过变量跟踪已处理的选项OPTIND
。
请尝试以下操作:
#!/bin/bash
f () {
printf "Intro OPTIND: %d\n" "$OPTIND"
while getopts ":a:b:" opt; do
printf "Current OPTIND: %d\n" "$OPTIND"
case $opt in
a)
echo "-a was triggered, Parameter: $OPTARG" >&2
;;
b)
echo "-b was triggered, Parameter: $OPTARG" >&2
;;
esac
done
printf "Exit OPTIND: %d\n" "$OPTIND"
}
echo "Run #1"
f "$@"
echo "Run #2"
f "$@"
屈服:
./test -a foo -b bar
Run #1
Intro OPTIND: 1
Current OPTIND: 3
-a was triggered, Parameter: foo
Current OPTIND: 5
-b was triggered, Parameter: bar
Exit OPTIND: 5
Run #2
Intro OPTIND: 5
Exit OPTIND: 5
因此你可以这样做:
OPTIND=1
在函数开始时。或者,根据具体情况,通常更好:
local OPTIND
如果OPTIND
不使用,当函数实现时,while 循环将永远进行下去。人们还可以使用它来恢复参数的处理,在失败或发生任何情况之后,如果 x 或 y 调用不同的函数,它将从上一个中断的地方恢复等等。