Bashのビルトインコマンド `declare` の使い方紹介(変数のglobalスコープ/参照渡し)

2019-03-09Bash, Cygwin, Linux, Ubuntu

またまた以下の内容の続きです。
まだまだ書き足りないけど今回で小休止を入れたいと思います。

  1. シェルスクリプトサンプルコードでよく見かけるtypesetやdeclareってなに?
  2. Bashのビルトインコマンド “declare” の使い方紹介(その1)
  3. Bashのビルトインコマンド “declare” の使い方紹介(その2)
  4. Bashのビルトインコマンド “declare” の使い方紹介(その3)
  5. Bashのビルトインコマンド declare の使い方紹介(変数のlocalスコープ)

前回 関数内で declare コマンドを使って宣言した変数は、自動的に local スコープになるというところで終わりました。
では、関数内の local スコープ変数をもっと広いスコープにするにはどうしたらよいでしょう?

-g オプション

declare コマンドのオプションに -g と言うものがあります。
デフォルトの local スコープよりさらに広い global スコープを設定してやることができます。
関数の内部で宣言した変数を関数の外部でも利用できるようになります。

# 関数を定義
f () {
  # -gオプションを付与して、globalスコープとする
  declare -g -i N=4
  echo "f: N=$N"
}

# 関数呼び出し前なので空
$ echo "N=$N"
N=

# 関数呼び出し。関数内の変数は当然見れる
$ f
f: N=4

# 関数呼び出し後はglobal変数が関数の外からも見れる
$ echo "N=$N"
N=4

# 変数を表示するだけの関数を作成
$ show_var () {
  echo "show_var : N=$N"
}

# 表示させてみる
$ show_var
show_var : N=4

# 再度宣言し直したらどうなるの?
$ f2 () {
  declare -g -i N=5
  echo "f2: N=$N"
}

# 上書きできちゃう
$ f2
f2: N=5

関数の外で参照できるだけでなく、他の関数からも参照できるようになります。
けれども、あんまり広いスコープの変数を使うことはないと思います。

ちなみに、 global変数とlocal変数は干渉し合わないのでしょうか?

# global変数を宣言する関数
$ func_global () {
  declare -g X=dog
  echo "$X"
}

# 呼び出し
$ func_global
dog

# global変数を表示
$ echo "$X"
dog

# local変数を宣言する関数
$ func_local () {
   declare X=cat
   echo "$X"
}

# 呼び出し前
$ echo "$X"
dog

# 呼び出し。関数内のX変数の値はたしかに変わっている
$ func_local
cat

# X変数の値は元に戻っている
$ echo "$X"
dog

同名の変数の干渉を避けることができます。

-n オプション

呼び出し元に結果を返す方法として global 変数を使う方法があるが、ちょっと変わった方法もあります。

-n オプションを使って変数の 参照渡し をする方法です。

# 関数呼び出し時の第一引数の参照をXという変数で定義
mod_var () {
  declare -n X="$1"

  # 値を書き換えちゃう
  X=100

  echo "mod_var: $X"
}

# 変数を宣言
declare Y=50

# 確認
$ echo "$Y"
50

# 関数呼び出し。値を書き換えてほしい変数を渡す
$ mod_var Y
mod_var: 100

# 変数の値を確認
$ echo "$Y"
100

# 関数内部で宣言した変数Xはlocalスコープなので当然見えない
$ echo "$X"

-n オプションは値を書き換えるときだけでなく、 連想配列配列 として宣言した変数を関数に引き渡す際に使います。

$ show_array () {
  declare -n ARRAY="$1"

  echo "-----"
  echo "${ARRAY[@]}"
  echo "-----"
}

$ declare -a N=(1 2 3 4 5)

# 関数の外では当然見れる
$ echo "${N[@]}"
1 2 3 4 5

# 関数の引数に配列を渡す
$ show_array N
-----
1 2 3 4 5
-----

$ show_hash () {
  declare -n HASH="$1"

  echo "-----"
  echo "${HASH[b]}"
  echo "-----"
}

$ declare -A N=([a]=x [b]=y [c]=z)
$ echo "${N[b]}"
y

$ show_hash N
-----
y
-----

2019-03-09Bash, Cygwin, Linux, Ubuntu