Bash のビルトインコマンド “declare” の使い方紹介(その3:読み取り専用)

2022-03-08Bash,CentOS,Cygwin,Linux,Ubuntu

以下のエントリの続き。

  1. シェルスクリプトサンプルコードでよく見かける typeset や declare ってなに? | ゲンゾウ用ポストイット
  2. Bash のビルトインコマンド "declare" の使い方紹介(その1) | ゲンゾウ用ポストイット
  3. Bash のビルトインコマンド "declare" の使い方紹介(その2) | ゲンゾウ用ポストイット

今回は 変数に型以外の情報を付与する機能 について。
他の言語で言う public とか finalキーワード をイメージしてもらえばよいです。

別の表現をすると、以下の属性を付与する機能と言えますね。

  • 変数のスコープ
  • 変数の読み取り専用(書き込み禁止)

declare コマンド以外でも設定できる内容であり、以下のキーワードとも絡めて話をしたいと思います。

  • readonly
  • export
  • local

検証環境

$ bash --version
GNU bash, バージョン 5.0.2(1)-release (x86_64-apple-darwin18.2.0)
Copyright (C) 2019 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

declare オプション(変数の利用を制限・開放するもの)

-r オプション

読込専用な変数を定義。 値を代入 したりや unset をできなくなる。

JavaPHP で言うところの final キーワードです。
変数の定義時にこちらのオプションを付与すると、定義時に代入した初期値は後から変更することはできない。

# -iオプションも一緒に付与して整数型の読み取り専用変数とする
$ declare -r -i NUM=50

# 値は正しく格納されている
$ echo "${NUM}"
50

# 代入できない
$ NUM=49
bash: NUM: 読み取り専用の変数です

# unsetで変数を開放もできない
$ unset NUM
bash: unset: NUM: 消去できません: variable は読み取り専用です

# -rオプション付与のタイミングで初期化し忘れた場合は悲惨
$ declare -r STR

# 当然空の状態
$ echo "${STR}"

# 代入できない。無意味な変数となってしまった。
$ STR="hogehoge"
bash: STR: 読み取り専用の変数です

連想配列型配列型 の変数にも -r オプションは適用される?

連想配列 で試してみる。

# -rと-aオプションを付与して、読み取り専用の連想配列となるかを確認
$ declare -r -A MAP=([i]=I [my]=MY [me]=ME)

# 変数を確認
$ declare -p MAP
declare -Ar MAP=([my]="MY" [me]="ME" [i]="I" )

# 要素を確認
$ echo "${MAP[me]}"
ME

# 書き換えてみる
$ MAP=hoge
bash: MAP: 読み取り専用の変数です

# 変わっていない
$ echo "${MAP[me]}"
ME

# 要素だけを書き換えてみる
$ MAP[me]=foo
bash: MAP: 読み取り専用の変数です

配列 でも試してみる。

# -rと-aオプションを付与して、読み取り専用の配列となるかを確認
$ declare -r -a ARRAY=(1 2 3)

# 要素を確認
$ echo "${ARRAY[@]}"
1 2 3

# 書き換えてみる
$ ARRAY=(4 5 6)
bash: ARRAY: 読み取り専用の変数です

# 要素だけを書き換えてみる
$ ARRAY[1]=4
bash: ARRAY: 読み取り専用の変数です

変数の再代入ができないだけではなく、 連想配列や配列に格納されている値も再代入不可能 です。
完全にイミュータブルであり、安心して使える変数となっています。

readonlyコマンド

declare コマンドを使わずに変数を読み取り専用として定義する方法もある。
Bash のビルトインコマンドの readonly コマンドを使う方法。

# -iオプションだけを付与して整数型変数を作成
$ declare -i NUM=50

# 値は正しく格納されている
$ echo "${NUM}"
50

# 再代入できる
$ NUM=35

# 値は変更された
$ echo "${NUM}"
35

# 後から読み取り専用の設定を行う
$ readonly NUM

# 書き換えてみる
$ NUM=20
bash: NUM: 読み取り専用の変数です

# 変わっていない
$ echo "${NUM}"
35

declare -rreadonly のそれぞれのコマンドで変数を定義し、 -p オプションを使って確認してみます。

$ declare -r A=a1
$ readonly B=b1

# `-p` オプションで変数がどのように定義されたものかを確認できる。
# 変数名を複数指定して、いっぺんに出力することもできる。
$ declare -p A B
declare -r A="a1"
declare -r B="b1"

どちらも declare -r コマンドを使って読み取り専用として定義された変数 となっていることがわかります。

それでは declare -rreadonly のそれぞれで定義された変数は全く同じ特性を持つのでしょうか?
関数の外で変数が定義された場合 は全く同じ特性となりますが、
関数の内で変数が定義された場合 にはスコープに違いが生じます。

※続きは次回。

ひとこと

まだまだ1エントリに割く時間がかかってる。
もっとサクサク情報公開していけたらいいのに。

2022-03-08Bash,CentOS,Cygwin,Linux,Ubuntu