私がやりたいことは以下の通りです。関数内で、変数に値を割り当てる必要があります。その名前は別の変数から取得されます。言い換えると:
func() {
#
# Here happens something that ultimately makes $arg="var_name"
#
declare -g ${arg}=5
}
func
echo ${var_name}; # Prints out "5"
上記のコードスニペットは、bash 4.2で適切に機能します。ただし、4.2より前のbashでは、declare
に-g
オプションがありません。 Googleで見つけたものはすべて、関数内でグローバル変数を定義するには、var=value
構文を使用する必要があると述べていますが、残念ながらvar
自体は別の変数に依存しています。 ${arg}=5
も機能しません。 (-bash: var_name=5: command not found
と表示されています。
興味深いことに、このすべての理由は、この関数が実際にスクリプトパラメータからグローバル変数を作成するためです。つまり、script --arg1=val
を実行すると、arg1
という名前の変数が自動的に作成され、値はval
になります。大量の定型コードを節約します。
Var = valueを文字列として作成し、bash組み込みコマンドeval
を使用して評価できます。
関数内のdeclare
が期待どおりに機能しません。関数で宣言された読み取り専用のグローバル変数が必要でした。私はこれを関数の中で試しましたが、うまくいきませんでした:
declare -r MY_VAR=1
しかし、これはうまくいきませんでした。 readonly
コマンドを使用した場合:
func() {
readonly MY_VAR=1
}
func
echo $MY_VAR
MY_VAR=2
これにより1が出力され、2番目の割り当てに対してエラー「MY_VAR:readonly variable」が発生します。
これはShell
とタグ付けされているため、declare
は限られたシェルのセットでのみ有効なキーワードであり、-gをサポートするかどうかは問題ではないことに注意することが重要です。一般的なBourne Shellでこの種のことを行うには、evalを使用するだけです。
eval ${arg}=5
-v
スイッチが組み込まれたprintf
が必要だと思います。
func() {
printf -v "$var" '5'
}
var=var_name
func
echo "$var_name"
5
を出力します。
"eval"(または任意の直接割り当て)を使用すると、特殊文字(または値の注入などの問題)が頭痛の種になる可能性があります。
「読む」だけで大成功
$ function assign_global() {
> local arg="$1"
> IFS="" read -d "" $arg <<<"$2"
>}
$ assign_global MYVAR 23
echo $MYVAR
23
$ assign_global MYVAR "\"'"
"'
もう少しハックしたくないものがあれば、declare -g
の代わりにexport
を使用してみてください。現在は環境変数であるという利点があります。
func() {
#
# Here happens something that ultimately makes $arg="var_name"
#
export ${arg}=5
}
func
echo ${var_name}; # Prints out "5"
残念ながら、これはまだ配列では機能しません。
関数定義内で配列変数を宣言するためのBashでは、次の例のように、local
コマンドをeval
コマンドと組み合わせて使用できます。
#!/bin/bash
function test
{
local -g $1
local array=( AA BB 'C C' DD)
eval ${1}='("${array[@]}")'
}
test VAR
echo VAR=${VAR[@]:1:2}
出力は次のようになります。
$./test.sh
VAR=BB C C
GNU bash、バージョン4.4.18
Evalは配列変数で動作します... evalステートメント内でローカル配列変数を単一引用符で囲む必要がありました。
以下は、ファイル(1番目の引数)を1行ずつ読み込み、それをget_fileの2番目の引数に渡された変数名にコピーします。
get_file () {
declare -a Array=();
while read line; do
Array=("${Array[@]}" "($line)")
done < ${1}
eval ${2}='("${Array[@]}")'
unset Array
}
declare -a my_array=();
get_file /etc/myfile.conf my_array
echo ${#my_array[@]}
たぶん、次の関数を使用して、bash(<4.2)でグローバル変数を動的に割り当てることができます。 2つ以上の引数が渡された場合、値は配列型になります。例えば
_set2globals variable_name arg1 [arg2] [arg3] [...]
ソース:
# dynamically set $variable_name($1)=$values($2...) to globals scope
function _set2globals()
{
if (( $# < 2 )); then
printf "$FUNCNAME: expect at least 2 argument, but %d you given.\n" $# >&2
exit 1
fi
local ___pattern_='^[_a-zA-Z][_0-9a-zA-Z]*$'
if [[ ! $1 =~ $___pattern_ ]]; then
printf "$FUNCNAME: invalid variable name: %s.\n" "$1" >&2
exit 1
fi
local __variable__name__=$1
shift
local ___v_
local ___values_=()
while (($# > 0)); do
___v_=\'${1//"'"/"'\''"}\'
___values_=("${___values_[@]}" "$___v_") # Push to array
shift
done
eval $__variable__name__=\("${___values_[@]}"\)
}