"getopts"を使用したBash機能は、最初の実行時にのみ機能します。

"getopts"を使用したBash機能は、最初の実行時にのみ機能します。

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
}

彼らはスクリプトを使用していますが、私はシェルから直接関数を定義します。

Bashを初めて起動して次の関数を定義すると、すべてがうまく機能f -a 123します-a was triggered, Parameter: 123。しかし、同じ行を2番目に実行すると、何も印刷されません

この動作の原因は何ですか? Bash 3.2と4.3で発生しますが、Zsh 5.1ではうまく動作します。この例はZshではなくBashのものなので、これは驚くべきことです。

答え1

bashはオプションを取得します環境変数の使用選択トレース処理の最後のオプションパラメーター。実際には、OPTIND同じシェルセッション内で呼び出されるたびに自動的にリセットされず、シェルが呼び出されたときgetoptsにのみリセットされます。したがって、getopts同じパラメータを使用して同じセッションで2回目に呼び出すと、何も変更されず、OPTIND操作がgetopts完了したと考え、何もしません。

OPTIND正しく動作するように手動でリセットできます。

$ OPTIND=1
$ f -a 123
-a was triggered, Parameter: 123

あるいは、関数をスクリプトに入れてスクリプトを複数回呼び出すだけです。


zshはオプションを取得します少し違う。OPTIND通常、シェル関数が終了するたびに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中断された部分から続きます。 2番目の電話が次の場合:

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が別の関数を呼び出すと、中断された部分から再開されます。

関連情報