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

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

我在 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 123prints -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 调用不同的函数,它将从上一个中断的地方恢复等等。

相关内容