理系学生日記

おまえはいつまで学生気分なのか

declare、typeset、そして local

declare とか typesetlocal まわりの使い方について。

まず、declaretypeset は、シノニムなので、この 2 つの差異は考えなくて良いと思います。このため、ここでは declare でひとまとめにして扱いたいと思います。 そういうわけで、declarelocal さえ区別できれば良いんですが、あんま変わらないんですよね。。。

属性の付与

declarelocal は、基本的に変数の宣言で使われますが、変数宣言とともに、その変数に対して属性を付与するときに使われます。

よく使われるのは、readonly のオプションですね。一度 -r 付きで宣言しておくと、その変数は readonly として扱われるようになり、それ以降で代入しようとするとエラーになります。

$ declare -r var="hoge"
$ var="fuga"
var: readonly variable

誤解されやすいのは、declare による属性の付与は、後出しが可能ってことでしょうか。 Google の出している Shell Style Guidereadonly を使った例が載っていますが、これ、declare を使っても同じようなことができます。

zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)"
if [[ -z "${zip_version}" ]]; then
  error_message
else
  readonly zip_version
fi
$ a="hoge"
$ typeset -r a
$ a="fuga"
a: readonly variable
$ echo $a
hoge

「整数型」っていう属性を与えることもできます。整数型という属性を与えることにより、浮動小数点が代入できなくなったりします。

$ x=3.14
bash: 3.14: syntax error: invalid arithmetic operator (error token is ".14")

また、expr(( )) とかを使わなくても、整数計算ができるようになったりします。

# declare -i を使った例
$ declare -i x
$ x=3+4
$ echo $x
7

# declare -i を使わなかった例
$ z=3+4
$ echo $z
3+4

ただ、この手の計算結果の格納は、分かりにくいので使わない気がします。 上記の例だと、x に文字列を代入しようとしたら計算結果が格納されているわけですが、前掲の通り、浮動小数点を代入しようとしたらエラーになるのは、あまり整合性が取れているとは思えないんですよね…。

この他、変数型が array であるとか、そういう属性を付けるときにも利用されたりします。

スコープ

というわけでスコープの話がでてくるんですが、local はわかりやすくて、その名前の通り「ローカル変数」を宣言できます。 Bash スクリプトのスタイルガイドは、結構 local 使えってところが多いですね。Google の Shell Style Guide もそうですし、以下の Defensive BASH Programming でも同様のことが言われています。

change_owner_of_file() {
    local filename=$1
    local user=$2
    local group=$3

    chown $user:$group $filename
}

HugeDomains.com より引用

スコープの話といえば、local の専売特許みたいなところがありますが、じつは typeset でもスコープの概念が導入されます。

foo (){
    declare FOO="bar"
}

bar (){
    foo
    echo $FOO
}

bar  # Prints nothing.

9.2. Typing variables: declare or typeset より引用

まぁ local の場合は、グローバル空間で宣言しようとするとエラーになるので、そのあたりは違いますね。

$ local p
bash: local: can only be used in a function
$ typeset p

参考文献