ネストされたループと配列

ネストされたループと配列

Bashでネストされたforループを使用する方法がわかりません。次のコードをネストされたforループに置き換えることはできますか?

~/
❯ cat sequential.sh
a=(1 2)
b=(3 4)
c=(5 6)

for e in "${a[@]}"
do
    echo $e
done

for e in "${b[@]}"
do
    echo $e
done

for e in "${c[@]}"
do
    echo $e
done

✦ ~/
❯ bash sequential.sh
1
2
3
4
5
6

答え1

ループに関連付けられている「ネスト」とは、通常、1つのループを別のループ内に配置して、内部ループの各繰り返しセットが外部ループの各繰り返しに対して実行されることを意味します。

for letter in a b c; do
    for digit in 1 2 3; do
        printf 'Letter = %s\tDigit = %s\n' "$letter" "$digit"
    done
done

...出力

Letter = a      Digit = 1
Letter = a      Digit = 2
Letter = a      Digit = 3
Letter = b      Digit = 1
Letter = b      Digit = 2
Letter = b      Digit = 3
Letter = c      Digit = 1
Letter = c      Digit = 2
Letter = c      Digit = 3

しかし、あなたの質問によると、まだ同じ出力が欲しいと仮定すると、入れ子にしたくないようです。代わりにあなたが望む結合する3つの別々のループを1つのループにまとめます。

これは、ループヘッダーに配列拡張を1つずつ追加して、繰り返すより大きな文字列セットを生成することによって行われます。

a=(1 2)
b=(3 4)
c=(5 6)

for item in "${a[@]}" "${b[@]}" "${c[@]}"
do
    printf 'item is "%s"\n' "$item"
done

これは、3つの小さな配列から別々の要素配列を作成し、より大きな配列を繰り返すのと同じ効果を持ちます。

a=(1 2)
b=(3 4)
c=(5 6)

combined=( "${a[@]}" "${b[@]}" "${c[@]}" )

# the above has the effect of setting the combined array as
# combined=( 1 2 3 4 5 6 )

for item in "${combined[@]}"; do
    printf 'item is "%s"\n' "$item"
done

または、より短い位置引数のリストを使用します。

a=(1 2)
b=(3 4)
c=(5 6)

set -- "${a[@]}" "${b[@]}" "${c[@]}"

for item do
    printf 'item is "%s"\n' "$item"
done

これは、ネストではなく、配列要素の3つの別々のセットをより大きなセットに結合することです。


printf必要なものが最小限のコードで3つの配列の要素を出力することであれば、すべてのパラメータが出力されるまでフォーマット文字列がすべてのパラメータに順次適用されるという事実を使用できます。

a=(1 2)
b=(3 4)
c=(5 6)

printf 'item is "%s"\n' "${a[@]}" "${b[@]}" "${c[@]}"

もう一つの方法は少し厄介ですがするネストされたループの場合、実際の配列を繰り返し、各配列を順番に処理します。bashシェルでは、ループを介してこれを行うことができます。名前名前参照変数を持つ配列:

a=(1 2)
b=(3 4)
c=(5 6)

for arrayname in a b c; do
    declare -n array="$arrayname"
    for item in "${array[@]}"; do
        printf 'item is "%s" (from original array "%s")\n' "$item" "$arrayname"
    done
done

ここでは、declare -n変数がarray現在の名前を持つすべての変数のように動作する必要があるとしばしば言います。arraynameそれからarrayそれを配列のように使います。これは、拡張値が$arrayname配列の名前であるために機能します。

これはやや珍しいプログラムですが、うまくbashいきます。また、別の配列処理関数を導入してコードをより複雑にすることもできます(この特別な場合は、このコードを書くのに適した方法ではありません。単一のステートメントを使用し、printfループをまったく使用しないことをお勧めします)。 :

a=(1 2)
b=(3 4)
c=(5 6)

process_array () {
    local -n array="$1"
    for item in "${array[@]}"; do
        printf 'item is "%s" (from original array "%s")\n' "$item" "$arrayname"
    done
}

for arrayname in a b c; do
    process_array "$arrayname"
done

これは「関数に配列を渡す」です(実際には、変数を参照するローカル名を持つ関数の配列名として使用される変数名を渡します)。

これにはいくつかの制限があります。たとえば、process_array名前参照変数を使用して他の関数を呼び出すことはできませんarray。また、見ることができますbash シェル関数には循環名参照がありますが、ksh にはありません。

答え2

✦ ~/
❯ cat nested.sh
a=(1 2)
b=(3 4)
c=(5 6)

for arr in "${a[@]}" "${b[@]}" "${c[@]}"
do
    for e in "${arr[@]}"
    do
        echo $e
    done
done

✦ ~/
❯ bash nested.sh
1
2
3
4
5
6

✦ ~/

関連情報