プログラミング言語としてのMakeでは、次のようなことができます。
# var-name.mk hello:=Hello, world. greeting-var:=hello $(info Hello, world.) $(info $(hello)) $(info $($(greeting-var))) .PHONY: end end:;@echo Done.
$ make -f var-name.mk
Hello, world.
Hello, world.
Hello, world.
Done.$
$($(greeting-var)) は、「『変数greeting-varが参照する値』の名前を持つ変数が参照する値」を意味するので、間接参照と言えます。メカニズムとしては、変数展開が入れ子で二回行われるわけです。
同じことをシェルでやってみます。
# var-name.sh hello="Hello, world." greeting_var=hello echo "Hello, world." echo $hello echo ${$greeting_var} echo Done.
$ sh var-name.sh
Hello, world.
Hello, world.
var-name.sh: line 8: ${$greeting_var}: bad substitution
Done.$
エラーになってしまいました。
evalを使う回避策があります。
# var-name-2.sh hello="Hello, world." greeting_var=hello echo "Hello, world." echo $hello eval "echo \${$greeting_var}" echo Done.
$ sh var-name-2.sh
Hello, world.
Hello, world.
Hello, world.
Done.$
うまくいきました。が、これは不格好ですね。bashなら、${!変数名} という書き方ができます。
# var-name-3.sh hello="Hello, world." greeting_var=hello echo "Hello, world." echo $hello echo ${!greeting_var} echo Done.
$ sh var-name-3.sh
Hello, world.
Hello, world.
Hello, world.
Done.$
次のような書き方もOKです。
# var-name-3.sh hello="Hello, world." greeting_var=hello echo "Hello, world." echo $hello echo ${!greeting_var} echo ${!greeting_var+Good morning.} echo ${!undefined_var-Good morning.} echo Done.
ここで、${変数名+値} は「変数が定義されているときは値を使う」を意味し、${変数名-値} は「変数が定義されていないきは値を使う」となります。変数名のところを !変数名 として間接参照と共に使えます。
$ sh var-name-4.sh
Hello, world.
Hello, world.
Hello, world.
Good morning.
Good morning.
Done.$
これを利用すると、ある名前の変数が定義されているかどうかを判定する関数を書けます。値が空文字列であっても「定義されている」とみなします。
# defined-demo.sh function defined { local var_name="$1" [ "$var_name" = "" ] && return 1 [ "${!var_name+DEFINED}" = "DEFINED" ] } foo="hello" bar="" if defined foo; then echo foo is defined; else echo foo is NOT defined; fi if defined bar; then echo bar is defined; else echo bar is NOT defined; fi unset foo echo echo foo is unset, then if defined foo; then echo foo is defined; else echo foo is NOT defined; fi if defined bar; then echo bar is defined; else echo bar is NOT defined; fi
$ sh defined-demo.sh
foo is defined
bar is definedfoo is unset, then
foo is NOT defined
bar is defined$