web-dev-qa-db-ja.com

間接参照を使用して配列を反復処理する方法は?

このコードを機能させるにはどうすればよいですか?

#!/bin/bash
ARRAYNAME='FRUITS'
FRUITS=( Apple BANANA ORANGE )
for FRUIT in ${!ARRAYNAME[@]}
do
    echo ${FRUIT}
done

このコード:

echo ${!ARRAYNAME[0]}

印刷Apple。私は似たようなことをしようとしていますが、「[@]」を使用して配列を反復処理しています。

前もって感謝します、

24
Neuquino

${!ARRAYNAME[@]}は「ARRAYNAMEのインデックス」を意味します。 bashのマニュアルページ に記載されているように、ARRAYNAMEが設定されているため、配列ではなく文字列として0が返されます。

evalを使用した解決策は次のとおりです。

#!/usr/bin/env bash

ARRAYNAME='FRUITS'
FRUITS=( Apple BANANA ORANGE )

eval array=\( \${${ARRAYNAME}[@]} \)

for fruit in "${array[@]}"; do
  echo ${fruit}
done

あなたが最初にやろうとしていたのは、 間接参照 を作成することでした。これらはbashバージョン2で導入され、シェルでリフレクションのような動作を実現しようとするときにevalの必要性を大幅に置き換えることを目的としていました。

配列で間接参照を使用するときに行う必要があるのは、変数名の推測に[@]を含めることです。

#!/usr/bin/env bash

ARRAYNAME='FRUITS'
FRUITS=( Apple BANANA ORANGE )

array="${ARRAYNAME}[@]"
for fruit in "${!array}"; do
  echo $fruit
done

とはいえ、この簡単な例で間接参照を使用するのは1つのことですが、Dennis Williamsonが提供するリンクに示されているように、実際のスクリプトで間接参照を使用することを躊躇する必要があります。これらは、コードを必要以上に混乱させることがほとんど保証されています。通常、連想配列で必要な機能を利用できます。

31
Tim Pote

これがevalなしでそれを行う方法です。

ここで説明されているBashトリック#2を参照してください: http://mywiki.wooledge.org/BashFAQ/006

Bash3以降で動作するようです。

#!/bin/bash

ARRAYNAME='FRUITS'
tmp=$ARRAYNAME[@]
FRUITS=( Apple BANANA ORANGE "STAR FRUIT" )
for FRUIT in "${!tmp}"
do
    echo "${FRUIT}"
done

関数を参照して配列を渡す方法を示す、より現実的な例を次に示します。

pretty_print_array () {
  local arrayname=$1
  local tmp=$arrayname[@]
  local array=( "${!tmp}" )
  local FS=', ' # Field seperator
  local var
  # Print each element enclosed in quotes and separated by $FS
  printf -v var "\"%s\"$FS" "${array[@]}"
  # Chop trailing $FS
  var=${var%$FS}
  echo "$arrayname=($var)"
}
FRUITS=( Apple BANANA ORANGE "STAR FRUIT" )
pretty_print_array FRUITS
# prints FRUITS=("Apple", "BANANA", "ORANGE", "STAR FRUIT")
16
Robin A. Meade

evalは、たとえばコマンド置換が含まれている場合でも、配列要素を含むコードを実行します。また、配列要素内のbashメタ文字を解釈することにより、配列要素を変更します。

これらの問題を回避するツールはdeclarereferenceです。declareの下のman bashを参照してください。

-n各名前にnameref属性を付けて、別の変数への名前参照にします。その他の変数は、nameの値によって定義されます。 -n属性自体を使用または変更する場合を除き、nameに対するすべての参照、割り当て、および属性の変更は、nameの値によって参照される変数に対して実行されます。 nameref属性を配列変数に適用することはできません。

#!/bin/bash
declare -n ARRAYNAME='FRUITS'
FRUITS=(Apple BANANA ORANGE "BITTER LEMON")
for FRUIT in "${ARRAYNAME[@]}"
do
    echo "${FRUIT}"
done
3
Hans Klünder

別の便利なユースケースを追加したかっただけです。私は別の、しかし関連する問題の解決策をウェブで探していました

ARRAYNAME=( FRUITS VEG )
FRUITS=( Apple BANANA ORANGE )
VEG=( CARROT CELERY CUCUMBER )
for I in "${ARRAYNAME[@]}"
do
    array="${I}[@]"
    for fruit in "${!array}"; do
        echo $fruit
    done
done
0
Felipe Alvarez

この答えは非常に遅くなりますが、これまでに提示されたものよりもはるかにクリーンなアプローチがあると思います(著者に敬意を表して)。

declare/localbash組み込みの-nオプションを使用することについてです。 (詳細については、bashにhelp declareと入力してください)。

だからここに行きます:

ARRAYNAME='FRUITS';
FRUITS=(Apple BANANA ORANGE);

# This is the critical addition. With help of option `-n` we declare
# variable `fruits` as indirect reference to another variable. Anytime
# we refer to ${fruits} we would actually refer to a variable whose
# name is stored in `fruits` variable:
declare -n fruits="${ARRAYNAME}";

# Here we use ${fruits} as ordinary variable, but in reality it refers
# to `FRUITS` variable:
for FRUIT in ${fruits[@]}; do
    echo "${FRUIT}";
done;

そして結果は次のとおりです。

Apple
BANANA
ORANGE
0
Cromax

単純なOPの質問にもかかわらず、これらの回答は、最も一般的な実際のユースケース、つまり、まだファイル名に展開されるべきではない空白またはワイルドカードを含む配列要素に対応しません。

_FRUITS=( Apple BANANA ORANGE 'not broken' '*.h')
ARRAYNAME=FRUITS
eval ARRAY=\(\${$ARRAYNAME[@]}\)

$ echo "${ARRAY[4]}"
broken
$ echo "${ARRAY[5]}"
config.h
$
_

これは機能します:

_FRUITS=( Apple BANANA ORANGE 'not broken' '*.h')
ARRAYNAME=FRUITS
eval ARRAY="(\"\${$ARRAYNAME[@]}\")"

$ echo "${ARRAY[3]}"
not broken
$ echo "${ARRAY[4]}"
*.h
$
_

_"$@"_ではなく_$@_を使用する習慣を身に付ける必要があるのと同じように、ファイル名の拡張が必要な​​場合や配列の可能性がないことがわかっている場合を除き、配列の拡張には常に_( )_内で引用してください。空白を含む要素。

これを行う:X=("${Y[@]}")

これではありません:X=(${Y[@]})

0
user246672