40

シェルスクリプトで動的変数名を生成して、次のようにループ内で個別の名前を持つファイルのセットを処理しようとしています。

#!/bin/bash

SAMPLE1='1-first.with.custom.name'
SAMPLE2='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ ))
do
  echo SAMPLE{$i}
done

私は出力を期待します:

1-first.with.custom.name
2-second.with.custom.name

しかし、私は得ました:

SAMPLE{1}
SAMPLE{2}

その場でvar名を生成することは可能ですか?

4

6 に答える 6

74

可変間接参照を利用する必要があります。

SAMPLE1='1-first.with.custom.name'
SAMPLE2='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ ))
do
   var="SAMPLE$i"
   echo ${!var}
done

Bashのマニュアルページの「パラメータ拡張」から:

「パラメーターの最初の文字が感嘆符(!)の場合、変数の間接化のレベルが導入されます。Bashは、パラメーターの残りの部分から形成された変数の値を変数の名前として使用します。次に、この変数が展開され、パラメータ自体の値ではなく、残りの置換で値が使用されます。これは、間接展開として知られています。」

于 2012-05-30T16:35:23.173 に答える
20

問題

iの値を、配列インデックスであるかのように使用しています。SAMPLE1とSAMPLE2は別々の変数であり、配列ではないため、そうではありません。

さらに、呼び出すときは、「SAMPLE」という単語にiecho SAMPLE{$i}の値を追加するだけです。このステートメントで間接参照している唯一の変数は$iです。これが、実行した結果を取得した理由です。

問題に対処する方法

これに対処する主な方法は2つあります。

  1. eval組み込み変数または間接変数展開を介した、補間された変数の多段階間接参照。
  2. 配列を反復処理するか、iを配列へのインデックスとして使用します。

evalによる間接参照

この状況で行う最も簡単なことは、evalを使用することです。

SAMPLE1='1-first.with.custom.name'
SAMPLE2='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ )); do
    eval echo \$SAMPLE${i}
done

これにより、 iの値が変数の末尾に追加され、結果の行が再処理されて、補間された変数名(SAMPLE1またはSAMPLE2など)が展開されます。

間接変数による間接参照

この質問に対する受け入れられた答えは次のとおりです。

SAMPLE1='1-first.with.custom.name'
SAMPLE2='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ ))
do
   var="SAMPLE$i"
   echo ${!var}
done

これは技術的には3ステップのプロセスです。まず、補間された変数名をvarに割り当て、次にvarに格納されている変数名を逆参照し、最後に結果を展開します。見た目は少しすっきりしていて、evalよりもこの構文に慣れている人もいますが、結果はほとんど同じです。

配列の反復

変数の補間を使用する代わりに、配列を反復処理することで、ループと展開の両方を単純化できます。例えば:

SAMPLE=('1-first.with.custom.name' '2-second.with.custom.name')
for i in "${SAMPLE[@]}"; do
    echo "$i"
done

これにより、他の方法に比べて利点が追加されました。具体的には:

  1. 複雑なループテストを指定する必要はありません。
  2. $ SAMPLE[$i]構文を介して個々の配列要素にアクセスします。
  3. $ {#SAMPLE}変数展開を使用して、要素の総数を取得できます。

元の例の実用的な同等性

3つの方法はすべて、元の質問で示した例で機能しますが、アレイソリューションは最も全体的な柔軟性を提供します。手元にあるデータに最適なものを選択してください。

于 2012-05-30T16:35:36.933 に答える
3

私の知る限りではありませんが、彼らは@johnshen64が言ったように。また、次のような配列を使用して問題を解決することもできます。

SAMPLE[1]='1-first.with.custom.name'
SAMPLE[2]='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ )) do
    echo ${SAMPLE[$i]}
done

SAMPLE[hello]インデックスも同様に機能するため、数値を使用する必要はないことに注意してください

于 2012-05-30T16:34:07.737 に答える
3

eval以下のように使用できます。

SAMPLE1='1-first.with.custom.name'
SAMPLE2='2-second.with.custom.name'

for (( i = 1; i <= 2; i++ ))
do
  eval echo \$SAMPLE$i
done
于 2012-05-30T16:35:39.633 に答える
2

独立した答えではなく、コメントにうまく収まらなかったミケルの答えへの単なる追加です。

ループ、+ =演算子、およびヒアドキュメントを使用して配列にデータを入力することもできます。

SAMPLE=()
while read; do SAMPLE+=("$REPLY"); done <<EOF
1-first.with.custom.name
2-second.with.custom.name
EOF

bash 4.0では、それは

readarray SAMPLE <<EOF
1-first.with.custom.name
2-second.with.custom.name
EOF
于 2012-05-30T21:28:45.733 に答える
0

eval "echo $ SAMPLE $ {i}"

于 2021-07-20T20:55:53.833 に答える