readonly とは
- 変数を編集禁止にする
- 他言語で言うところの
const
っぽい使い方ができる- グローバル変数を作りがちなシェルスクリプトにおいて有益なコマンド。コーディング規約 にもよく出てくる
使用例
$ MY_MSG='OK Google'
$ readonly MY_MSG
$ MY_MSG='Hey Alexa'
-bash: MY_MSG: readonly variable
書き換えできないことがわかる。
OLDPWD を readonly したら?
OLDPWD
: cd
する直前のディレクトリが保存される変数
$ pwd
/var
$ cd /opt
$ echo $OLDPWD
/var
こいつに readonly をつけると…
書き換えできない
$ readonly OLDPWD
$ cd ~
-bash: OLDPWD: readonly variable
$ cd /tmp
-bash: OLDPWD: readonly variable
cd
時に OLDPWD
を更新しようとするが、readonly でガードされているために cd
するたびにエラーメッセージが表示されるようになる。これは困った。
readonly を外すことはできる?
一度セットされた readonly は、基本的にシェルにログインし直すまで外すことができない。
しかし、Stackoverflow にて強引に外す方法が紹介されていたので、上記 OLDPWD の readonly を外せるかどうか試してみた。
強引に外す
# OLDPWD に readonly がついていることを確認
$ cd ~
-bash: OLDPWD: readonly variable
# gdb で Bash の組み込み関数を直接呼ぶ
$ sudo gdb << EOF
attach $$
call unbind_variable("OLDPWD")
detach
EOF
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-92.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <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. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb) Attaching to process 5224
Reading symbols from /bin/bash...(no debugging symbols found)...done.
Reading symbols from /lib64/libtinfo.so.5...(no debugging symbols found)...done.
Loaded symbols for /lib64/libtinfo.so.5
Reading symbols from /lib64/libdl.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libdl.so.2
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
Reading symbols from /lib64/libnss_files.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/libnss_files.so.2
0x00007f99ba2d165e in waitpid () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install bash-4.1.2-33.el6_7.1.x86_64
(gdb) $1 = 0
(gdb) Detaching from program: /bin/bash, process 5224
(gdb) quit
gdb で行っていることは以下の3つ。
-
attach $$
: 今動いている Bash を gdb の操作対象にする。$$
はシェルの PID -
call unbind_variable("OLDPWD")
: Bash のソース によると「シェル変数 (OLDPWD) をアンセットする」 -
detach
: gdb から開放する
以上を行うことで、 cd
しても怒られなくなった。
# OLDPWD を確認
$ echo $OLDPWD
# cd しても怒られない
$ cd /tmp
$
readonly を外すというよりは、変数そのものを消し去ったようだ。
備考
上記 Stackoverflow にも書かれているが、変数のアンセットに関しては unset
というコマンドが用意されている。
一般的な変数に関しては unset 変数名
で事足りるが、readonly された変数に対してはやはり使えない。
# readonly なし
$ NO_READONLY='foo'
# 消せる
$ unset NO_READONLY
$ echo $NO_READONLY
$
# readonly あり
$ READONLY='bar'
$ readonly READONLY
# 消せない
$ unset READONLY
-bash: unset: READONLY: cannot unset: readonly variable
ということで、readonly 変数に関しては gdb を使った方法しかなさそう。
まとめ
- 変数に readonly をつけると書き換えられなくなる
- readonly を一度つけると原則取り消すことができない
- gdb で Bash の
unbind_variable
を呼び、強制的に変数を消去することで readonly を取り消すことができる