Edited at

Bash 5.0の新機能:localvar_inherit

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を使って呼び出し先にコピーするのが便利そうです。