変数配列名を持つ bash ループ連想配列

変数配列名を持つ bash ループ連想配列

連想配列が多いですが、ループを1つだけ使用したいと思います。与えられた名前でループ配列を選択する

変数を使用して配列名の一部を選択/構築し、その名前で繰り返したいが機能しません。

OUTPUT3とOUTPUT4に似ていますが、構文が正しくありません。

出力3の場合は「bash error replacement」と表示されます。

出力4の場合、「配列名のみがあり、0」が表示されます。

#!/bin/bash
clear

declare -A a1 a2 a3

a1['1']="1-1V"

a2['1']="2-1V"
a2['2']="2-2V"

a3['1']="3-1V"
a3['2']="3-2V"
a3['3']="3-3V"

# 1 OUTPUT WORKS
for i in ${!a1[*]}
do
echo -e "$i : ${a1[$i]}"
done

# 2 OUTPUT WORKS
for i in ${!a2[*]}
do
echo -e "$i : ${a2[$i]}"
done

# 3 OUTPUT - WRONG SYNTAX
selectkey="3"
for i in ${!a$selectkey[@]}
do
echo -e "$i : ${a$selectkey[$i]}"
done

# 4 OUTPUT - WRONG SYNTAX
key="3"
aselect="a${key}[*]"
# THIS ECHO WORKS
echo -e "ARRAY: ${!aselect}"

for i in ${!aselect[@]}
do
echo -e "$i : ${aselect[$i]}"
done

修正する

解決策はnameref|declare -n

これは今私にとって効果的です。

#!/bin/bash
clear

declare -A a1 a2 a3

a1['1']="1-1V"

a2['1']="2-1V"
a2['2']="2-2V"

a3['1']="3-1V"
a3['2']="3-2V"
a3['3']="3-3V"

varname="a3"
counter=1

declare -n refname=${varname}

for i in "${!refname[@]}"
do
echo -e "$counter ${refname[$counter]}"
counter=$((counter+1))
done

答え1

「nameref」の使用:declare -n a=b変数をa変数のエイリアスにしますb

# 3 OUTPUT - use a "nameref"
selectkey="3"
declare -n ary="a$selectkey"

for i in "${!ary[@]}"
do
    echo "$i : ${ary[$i]}"
done

出力

3 : 3-3V
2 : 3-2V
1 : 3-1V

連想配列は本質的に順序がない。

#4の場合に記載されているように「間接拡張」を使用しています。シェルパラメータ拡張ただし、この手法を使用して配列のインデックスを取得することはできません。

答え2

bash多次元配列はありません。ただし、連想配列とキー連結(結合文字列として表示key1)を使用して実装できます。ここにいくつかの例があります。key2key1,key2

スタートシナリオを考えると

declare -A a

a[1,1]='1-1V'
a[2,1]='2-1V' a[2,2]='2-2V'
a[3,1]='3-1V' a[3,2]='3-2V' a[3,3]='3-3V'

すべてのa[1,*]値:

for i in $(printf "%s\n" "${!a[@]}" | awk -F, -vk='1' '$1==k {print $2}')
do
    printf "%s: %s\n" "$i" "${a[1,$i]}"
done

出力

1: 1-1V

すべてのa[*,2]値:

for i in $(printf "%s\n" "${!a[@]}" | awk -F, -vk='2' '$2==k {print $1}')
do
    printf "%s: %s\n" "$i" "${a[$i,2]}"
done

出力

3: 3-2V
2: 2-2V

キーセットを生成する別の方法:

for i in $(printf "%s\n" "${!a[@]}" | awk -F, -vk='1' '$2==k')
do
    printf "%s: %s\n" "$i" "${a[$i]}";
done

出力

1,1: 1-1V
3,1: 3-1V
2,1: 2-1V

すべての場合、キーは数字順になっていません。必要に応じてprintf | awkパイプを渡すように設定を変更できますsort -n(数値キーの仮定)。

awkフィールド番号と必要なキー値を指定するように設定を一般化することもできます。これはおそらく、関数内のキー生成を隠すことによってカプセル化するのに役立ちます。

awk -F, -vfield=2 -vkey=1 '$field==key'

答え3

インデックスが数値の場合は、連想配列ではなく通常の配列を使用する方が良いです。どちらも希薄ですが、通常の配列を使用すると数値順にキーを繰り返すことができますが、連想配列を使用すると本質的にランダムです。 (他の言語では、希少な配列を取得するには連想配列が必要な場合があります。)比較:

$ declare -A a=([11]=a [22]=b [33]=c ) 
$ echo "${a[@]}"
b a c
$ declare -a b=([11]=a [22]=b [33]=c)
$ echo "${b[@]}"
a b c

その後、インデックスが数値で配列がリーンでない場合は、連想配列を使用して2D配列を偽にし、手動で繰り返してインデックスを繰り返すことができます。

declare -A a
a[1,1]="1-1V"

a[2,1]="2-1V"
a[2,2]="2-2V"

a[3,1]="3-1V"
a[3,2]="3-2V"
a[3,3]="3-3V"

x=3
i=1
while [[ ${a[$x,$i]+set} == set ]]; do
    echo "${a[$x,$i]}"
    ((i++))
done

(もちろん、「存在しない」要素を特定の値に設定してから、ループ内でその値を無視してスパース配列をシミュレートできます。)

Kshは実際には2D配列または少なくともネストされたリストもサポートします。

a[1][1]="1-1V"

a[2][1]="2-1V"
a[2][2]="2-2V"

a[3][1]="3-1V"
a[3][2]="3-2V"
a[3][3]="3-3V"

x=3
for x in "${a[x][@]}"; do
    echo "$x"
done

これがうまくいかなくても:

for x in "${a[@][1]}"; do
    echo "$x"
done

しかし、複雑なデータ構造の必要性は、シェルから他のプログラミング言語に切り替えることを検討するのが有用である可能性がある状況の1つです。

${!array[*]}いかなる場合でも、または${!array[@]}(組み込みまたは除外!)を使用しないでください。"${!array[@]}"その逆です。これは、空白を含む値をそのまま維持する唯一の方法だからです。たとえばz=("foo bar" "zoom")、次を使用してfor x in ${z[*]}; do echo $x; done同じものと比較します。"${z[*]}""${z[@]}"

関連情報