带有 `getopts` 的 Bash 函数仅在第一次运行时有效

sha*_*ker 11 bash function getopts

f根据此处的示例(在“带参数的选项”下)在 Bash 中定义了该函数:

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
}
Run Code Online (Sandbox Code Playgroud)

虽然他们使用脚本,但我直接在 shell 中定义函数。

当我第一次启动 Bash 并定义函数时,一切正常:f -a 123prints -a was triggered, Parameter: 123。但是当我第二次运行完全相同的行时,没有打印任何内容

是什么导致了这种行为?它发生在 Bash 3.2 和 4.3 中,但它在 Zsh 5.1 中工作正常。这令人惊讶,因为该示例应该用于 Bash,而不是用于 Zsh。

cuo*_*glm 17

bash getopts使用环境变量OPTIND来跟踪处理的最后一个选项参数。OPTIND每次getopts在同一个 shell 会话中调用时不会自动重置的事实,只有在调用 shell 时才自动重置。所以从你第二次getopts在同一个会话中使用相同的参数调用,OPTIND没有改变,getopts认为它已经完成了工作并且什么都不做。

您可以OPTIND手动重置以使其工作:

$ OPTIND=1
$ f -a 123
-a was triggered, Parameter: 123
Run Code Online (Sandbox Code Playgroud)

或者只是将函数放入脚本并多次调用脚本。


zsh getopts略有不同。OPTIND每次退出 shell 函数时,通常都会重置为 1。


小智 6

在任何函数中声明局部变量是一种上帝的习惯。如果您声明 $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
}
Run Code Online (Sandbox Code Playgroud)

  • 只是声明对我不起作用。我不得不在 `while getopts...` 语句之前放置 `unset opt OPTARG OPTIND`,这样每个变量的值都没有设置。如果我不这样做而只是做了声明,因为在给定的 bash 会话中,一旦存在 `OPTIND`,我已经使用了 `getopts`,它保持不变 (2认同)