このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

bashの間接参照変数

プログラミング言語としての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 defined

foo is unset, then
foo is NOT defined
bar is defined

$