bash: インデックスを保存しながら関数のパラメータから配列名を取得する

bash: インデックスを保存しながら関数のパラメータから配列名を取得する

選択した要素のインデックスを表示する関数があります。関数にパラメータを渡して、それを配列名として使用しようとしています。これは機能します:

getIndex() {
arrname=$1[@]
b=("${!arrname}")
index=1; while ((index<${#b[@]})); do
    if [[ "${b[$index]}" = "$VALUE" ]]; then
        echo "index is $index"; return
    fi  
        ((index++)); done

}

しかし、この関数に渡す名前の配列には、最初の要素のインデックスとしてインデックス 1 があります (配列内のパターンを取得する行番号と同様のインデックスが必要です)。

a=1
while read line; do
    if [[ $line =~ ^[0-9] ]]; then
        avg[$a]=`echo $line | awk '{print $6}'`
        ((a++));    
    fi

また、関数 getIndex() を実行している場合、配列の最初の要素はインデックス 0 から始まります。

そこで質問です。配列のインデックスを保存したまま、関数のパラメータに配列名を渡す方法はありますか? それとも、それを忘れて関数の回答に +1 を追加する必要があるだけかもしれません。

答え1

次の構文を使用しています:

b=("${!arrname}")

これにより、価値観配列の、新しい配列を作成b[]するbashのデフォルトの配列インデックスは0から始まります配列のコピーを適切に初期化するには、インデックスを復元する必要があります(たとえば、evalの出力を解析または -ingするなどdeclare -p arrname)。

コピーを作るよりも、より良いアプローチは、インデックス値ではなく、それらを使用して配列を反復処理します。このアプローチは、スパース配列、またはゼロベース以外の標準配列 (および bash4 連想配列) でも機能します。

問題は (常にあるわけではありませんが)、 が!二重の役割を果たしていることです。間接参照での使用は、${!name}配列インデックスの拡張での使用と互換性がないため${!arrname[@]}、 を使用する必要がありますeval

これを実装する修正バージョンを次に示します。

getIndex2() {
  local arrname=$1 iidx idxs index ival val
  printf -v iidx '"${!%s[@]}"' "$arrname"
  eval "idxs=($iidx)"
  for index in "${idxs[@]}"; do
    printf -v ival '${%s[%s]}' "$arrname" "$index"
    eval "val=$ival"
    if [[ "${val}" = "$VALUE" ]]; then
        echo "index is $index"; return   
    fi
  done
}

printf -v var ...読みやすさを保つために (bash-3.1+)を使用していることに注意してくださいeval。インデックスは配列に展開されますが、これは必ずしも必要ではなく、フラットなリストでもかまいません。

参照バッシュFAQ/006

答え2

Bashの配列はゼロベースです。そうなのです。知る配列は常に1から始まる番号でインデックス付けされるので、結果に+1を追加するだけです。関数に2番目の引数を追加して、どの番号から開始するか、または結果に追加するかを伝え、2番目の引数がない場合に適切なデフォルトを追加します。または、次のようにインデックスをループします。スプラティックさん示唆します。

関連情報