Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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をそれほど使うとは思わないし、その中でも特殊なケースなので、非互換による影響はほとんどないのではないでしょうか。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした