連想配列以外にも、Bashで動的変数を実現する方法はいくつかあります。これらすべての手法にはリスクが存在することに注意してください。リスクについては、この回答の最後で説明します。
次の例では、初期値がであるi=37
という名前の変数にエイリアスを設定することを想定しています。var_37
lolilol
方法1.「ポインター」変数の使用
Cポインターとは異なり、変数の名前を間接変数に格納するだけです。次に、Bashにはエイリアス変数を読み取るための構文${!name}
があります。名前が変数の値である変数の値に展開されますname
。これは2段階の展開と考えることができます。${!name}
展開先は$var_37
、展開先はlolilol
です。
name="var_$i"
echo "$name" # outputs “var_37”
echo "${!name}" # outputs “lolilol”
echo "${!name%lol}" # outputs “loli”
# etc.
残念ながら、エイリアスされた変数を変更するための対応する構文はありません。代わりに、次のいずれかの方法で割り当てを行うことができます。
1a。割り当てeval
eval
は悪ですが、私たちの目標を達成するための最も簡単で移植可能な方法でもあります。割り当ての右側は2回評価されるため、慎重にエスケープする必要があります。これを行う簡単で体系的な方法は、事前に右側を評価する(またはを使用するprintf %q
)ことです。
そして、左側が有効な変数名、またはインデックス付きの名前であることを手動で確認する必要があります(それがそうだったとしevil_code #
たら?)。対照的に、以下の他のすべてのメソッドは自動的に強制します。
# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit
value='babibab'
eval "$name"='$value' # carefully escape the right-hand side!
echo "$var_37" # outputs “babibab”
欠点:
- 変数名の有効性はチェックしません。
eval
悪です。
eval
悪です。
eval
悪です。
1b。割り当てread
read
組み込みは、あなたが名前、ここで、文字列と組み合わせて活用することができるという事実を与えているの変数に値を割り当てることができます:
IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37" # outputs “babibab\n”
IFS
部分とオプションは-r
そのままオプションは、一方で値は、割り当てられていることを確認して-d ''
複数行の値を割り当てることができます。この最後のオプションのため、コマンドはゼロ以外の終了コードで戻ります。
ここではヒア文字列を使用しているため、値に改行文字が追加されることに注意してください。
欠点:
- ややあいまい。
- ゼロ以外の終了コードで戻ります。
- 値に改行を追加します。
1c。割り当てprintf
Bash 3.1(2005年リリース)以降、printf
ビルトインは名前が指定された変数にその結果を割り当てることもできます。以前のソリューションとは対照的に、それは機能するだけで、物事をエスケープしたり、分割を防止したりするなどの追加の作業は必要ありません。
printf -v "$name" '%s' 'babibab'
echo "$var_37" # outputs “babibab”
欠点:
方法2.「参照」変数を使用する
Bash 4.3(2014年にリリース)以降、declare
組み込みには、-n
C ++参照のように、別の変数への「名前参照」である変数を作成するオプションがあります。方法1と同様に、参照にはエイリアス変数の名前が格納されますが、参照がアクセスされるたびに(読み取りまたは割り当てのいずれかで)、Bashは自動的に間接参照を解決します。
さらに、Bashには、参照自体の値を取得するための特別で非常にわかりにくい構文があり、自分で判断します${!ref}
。
declare -n ref="var_$i"
echo "${!ref}" # outputs “var_37”
echo "$ref" # outputs “lolilol”
ref='babibab'
echo "$var_37" # outputs “babibab”
これは、以下で説明する落とし穴を回避しませんが、少なくとも構文を簡単にします。
欠点:
リスク
これらすべてのエイリアシング手法にはいくつかのリスクがあります。1つ目は、間接参照を解決するたびに(読み取りまたは割り当てのために)任意のコードを実行することです。実際、のようなスカラー変数名の代わりにvar_37
、のような配列の添え字に別名を付けることもできますarr[42]
。ただし、Bashは角括弧の内容を必要とするたびに評価するため、エイリアシングarr[$(do_evil)]
は予期しない影響を及ぼします。そのため、これらの手法は、エイリアスの出所を制御する場合にのみ使用してください。
function guillemots() {
declare -n var="$1"
var="«${var}»"
}
arr=( aaa bbb ccc )
guillemots 'arr[1]' # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]' # writes twice into date.out
# (once when expanding var, once when assigning to it)
2番目のリスクは、循環エイリアスを作成することです。Bash変数はスコープではなく名前で識別されるため、誤ってそれ自体にエイリアスを作成する可能性があります(それを囲んでいるスコープから変数にエイリアスを作成すると考えている場合)。これは特に、共通の変数名(などvar
)を使用するときに発生する可能性があります。結果として、エイリアス変数の名前を制御する場合にのみ、これらの手法を使用してください。
function guillemots() {
# var is intended to be local to the function,
# aliasing a variable which comes from outside
declare -n var="$1"
var="«${var}»"
}
var='lolilol'
guillemots var # Bash warnings: “var: circular name reference”
echo "$var" # outputs anything!
ソース: