2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Bash 5.0の新機能:namerefの挙動の変更

Last updated at Posted at 2019-01-12

Bash 5.0では、namerefの名前解決で非互換の挙動があると言われています。

リリースアナウンスでは次のように書かれています。

There are a few incompatible changes between bash-4.4 and bash-5.0. The
changes to how nameref variables are resolved means that some uses of
namerefs will behave differently, though I have tried to minimize the
compatibility issues.

「bash 5.0のNEWSファイル私訳」にも次のようにあります。

i. 関数内でnamerefの名前解決のループは、グローバルスコープでの名前の変数
に解決されるようになりました。

そもそもnamerefを知らないと変更点もわからないので、順に説明します。

おさらい①:namerefって?

namarefはBash 4.3から導入された機能です。ksh由来のようで、AIXのマニュアルでは「名前参照」と、Solarisのマニュアルでは「nameref」と表記されているようです。

namerefは、ほかの変数を参照する変数を作る機能です。変数のエイリアス(別名)を付けるときなどに使います。

namerefはdeclare -nlocal -nなど、変数宣言のときに-nオプションを指定して作ります。namerefな変数に、ほかの変数名を文字列として代入すると、エイリアスのように動きます。

$ aaa=3                 # 変数aaaに3を代入
$ declare -n bbb        # namerefな変数bbbを宣言
$ bbb=aaa               # bbbに'aaa'を代入($aaaではない)
$ echo $bbb             # bbbの内容は?
3

おさらい②:namerefの使い道

namerefの使い道として自分が考えたのが、関数から関数に値を渡すときに、変数名を渡す方法です。

たとえば、関数から関数に配列を渡すときには、引数や標準入力ではそのままでは渡せません。そこでダイナミックスコープを使って渡す方法があります。

foo() {
  local ary=('a b' c d)         # 配列変数aryを宣言
  bar
}

bar() {
  echo ${ary[0]}                # fooで宣言された配列変数aryを参照
}

foo                             # 「a b」と表示される

ただbarから暗黙的に変数aryを参照するのがいまいちです。そこで、namerefを使って変数名を渡すことで、barではfooで宣言された変数名の知識が不要になります。

foo() {
  local ary=('a b' c d)
  bar ary               # “ary”という変数名を文字列で引数に
}

bar() {
  local -n var=$1       # nameref変数varに1つめの引数の文字列(変数名)を代入
  echo ${var[0]}
}

foo                      # 「a b」と表示される

namerefの挙動の違い

さて、Bash 5.0で挙動が変わったのはどのあたりでしょうか。

「関数内でのnamerefの名前解決のループ(A nameref name resolution loop in a function)」ということなので、namerefで循環参照を作ってみます。

aaa=7                           # グローバル変数aaa

foo() {
  local -n aaa                  # ローカルなnameref変数aaa
  aaa=aaa                       # aaaからaaa自身を参照
  echo $aaa
}

foo

これをBash 4.4で実行すると、循環参照の警告が表示されたあと、echo $aaaでは何も表示されません。

$ bash ./n.sh
./n.sh: line 6: warning: aaa: circular name reference

Bash 5.0で実行すると、循環参照の警告が表示されたあと、echo $aaaでグローバル変数aaaの値である「7」が表示されます。

$ ./bash ./n.sh
./n.sh: line 6: warning: aaa: circular name reference
7

結論

そもそも、Bashでnamerefをそれほど使うとは思わないし、その中でも特殊なケースなので、非互換による影響はほとんどないのではないでしょうか。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?