Bash 5.0のNEWSファイルをさくっと翻訳したエントリー「bash 5.0のNEWSファイル私訳」を先日書きました。
この中に、localvar_inheritという新しいshoptオプションが追加されたことが書かれています。
o. 新しいshoptオプション:localvar_inherit。設定すると、ローカル変数
は、直前のスコープにある同じ名前の変数の値を継承します。
自分で訳しておいてなんですが、わかりにくいですね。
Bash 5.0のmanpageでは、こう説明されています。
localvar_inherit
If set, local variables inherit the value and attributes
of a variable of the same name that exists at a previous
scope before any new value is assigned. The nameref
attribute is not inherited.
やはりわかりにくいですね。
そこで、ちょこっと調べて試してみた結果を、順を追って説明します。
おさらい①:関数
多くのプログラミング言語と同じく、Bashでも関数を定義できます。定義した関数はコマンドとして実行できます。
$ cat hello.sh
hello() { # helloという関数を定義
echo hello
}
hello # helloを実行
$ bash ./hello.sh
hello
おさらい②:ローカル変数
単純なシェルでは変数はグローバル変数しかありません。しかしBashでは、local
コマンドによって関数内のローカル変数を定義できます。
$ cat localvar.sh
foo() {
local aaa # aaaというローカル変数を定義
aaa=5 # aaaに5を代入
}
aaa=3 # aaaに3を代入
foo # fooを実行
echo $aaa # その後でaaaの値は?
$ bash ./localvar.sh
3
おさらい③:ローカル変数はダイナミックスコープ
Bashのローカル変数のスコープは、呼び出し先の関数でも有効です。Common Lispのスペシャル変数や、Emacs Lispのデフォルトのダイナミックスコープ変数、Perlのlocalなどに似ています。
次のように、関数fooと、fooから呼び出す関数barを定義します。すると、fooで定義したローカル変数aaaがbarからも参照できます。
$ cat scope.sh
foo() {
local aaa=3 # aaaというローカル変数を定義して値を3に
bar
}
bar() {
echo $aaa # barでのaaaの値は?
}
foo
$ bash ./scope.sh
3
同じ変数aaaを参照しているので、barで値を変更すると、fooでも変更されています。
$ cat scope2.sh
foo() {
local aaa=3 # aaaというローカル変数を定義して値を3に
bar
echo $aaa # barを呼び出した後でaaaの値は?
}
bar() {
aaa=5 # barでaaaに5を代入
}
foo
$ bash ./scope2.sh
5
ようやく本題:localvar_inherit
では、Bash 5.0の新機能であるlocalvar_inheritを見てみましょう。
localvar_inheritをオンにすると、呼び出し元の変数と同じ名前の変数をlocal
で定義したときに、呼び出し元の変数の値と属性が コピー されます。
次の例では、fooとbarで同じ名前の変数aaaを定義しています。barではaaaの値を代入していないのに、fooでaaaに設定した値が参照されます。
$ cat inherit.sh
shopt -s localvar_inherit # localvar_inheritをオンに
foo() {
local aaa=3 # fooでローカル変数aaaを定義
bar
}
bar() {
local aaa # barでもaaaを定義。値は代入しない
echo $aaa # aaaの値は?
}
foo
$ bash ./inherit.sh
3
ダイナミックスコープと違い、同じ変数を参照するわけではありません。次のように、barでaaaの値を変更しても、fooのaaaの値は変わりません。
$ cat inherit.sh
shopt -s localvar_inherit
foo() {
local aaa=3
bar
echo $aaa # barを呼び出した後でaaaの値は?
}
bar() {
local aaa
aaa=5 # barでaaaに5を代入
}
foo
$ bash ./inherit.sh
3
用途
さて、これをどういうところで使うと便利でしょうか。
普通の変数の値であれば、引数などで渡せば十分です。ただし、配列は引数などでは渡しづらいので、localvar_inheritを使って呼び出し先にコピーするのが便利そうです。