bashで読み取り専用変数を設定解除するにはどうすればよいですか?
$ readonly PI=3.14
$ unset PI
bash: PI: readonly variable
それとも不可能ですか?
実際には、読み取り専用変数の設定を解除できます。しかし、これはハッキーな方法であることを警告する必要があります。推奨事項ではなく、情報としてのみこの回答を追加します。自己責任。 ubuntu 13.04、bash 4.2.45でテスト済み。
この方法では、bashのソースコードを少し知っている必要があり、 this answerから継承されます。
$ readonly PI=3.14
$ unset PI
-bash: unset: PI: cannot unset: readonly variable
$ cat << EOF| Sudo gdb
attach $$
call unbind_variable("PI")
detach
EOF
$ echo $PI
$
TMOUTの設定を解除して(自動ログアウトを無効にする)ために上記のgdbハックを試しましたが、TMOUTが読み取り専用に設定されているマシンでは、Sudoを使用できません。しかし、bashプロセスを所有しているので、Sudoは必要ありません。しかし、構文は、現在使用しているマシンではまったく機能しませんでした。
ただし、これはうまくいきました(.bashrcファイルに入れました):
# Disable the stupid auto-logout
unset TMOUT > /dev/null 2>&1
if [ $? -ne 0 ]; then
gdb <<EOF > /dev/null 2>&1
attach $$
call unbind_variable("TMOUT")
detach
quit
EOF
fi
Manページによると:
unset [-fv] [name ...]
... Read-only variables may not be
unset. ...
変数をまだエクスポートしていない場合は、exec "$0" "$@"
を使用してシェルを再起動できます。もちろん、エクスポートされていない他のすべての変数も失われます。 exec
なしで新しいシェルを起動すると、そのシェルの読み取り専用プロパティが失われるようです。
しかし、単純な構文では:
gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
機能として、いくつかの改善があります:
destroy
関数:または可変メタデータをチェックする方法...
destroy () {
local -n variable=$1
declare -p $1 &>/dev/null || return -1 # Return if variable not exist
local reslne result flags=${variable@a}
[ -z "$flags" ] || [ "${flags//*r*}" ] && {
unset $1 # Don't run gdb if variable is not readonly.
return $?
}
while read resline; do
[ "$resline" ] && [ -z "${resline%\$1 = *}" ] &&
result=${resline##*1 = }
done < <(
gdb 2>&1 -ex 'call unbind_variable("'$1'")' --pid=$$ --batch
)
return $result
}
これを、サンプルのdestroy.bash
というbashソースファイルにコピーできます。
1 destroy () { 2 local -n variable=$1 3 declare -p $1 &>/dev/null || return -1 # Return if variable not exist 4 local reslne result flags=${variable@a} 5 [ -z "$flags" ] || [ "${flags//*r*}" ] && { 6 unset $1 # Don't run gdb if variable is not readonly. 7 return $? 8 } 9 while read resline; do 10 [ "$resline" ] && [ -z "${resline%\$1 = *}" ] && 11 result=${resline##*1 = } 12 done < <( 13 gdb 2>&1 -ex 'call unbind_variable("'$1'")' --pid=$$ --batch 14 ) 15 return $result 16 }
$flags
変数に格納します。unset
の代わりにgdb
を実行しますwhile read ... result= ... done
gdb
出力のcall unbind
の戻りコードを取得--pid
および--ex
を使用した13行目のgdb
構文(gdb --help
を参照)。$result
のcall unbind
コマンドを返します。source destroy.bash
# 1st with any regular (read-write) variable:
declare PI=$(bc -l <<<'4*a(1)')
echo $PI
3.14159265358979323844
echo ${PI@a} # flags
declare -p PI
declare -- PI="3.14159265358979323844"
destroy PI
echo $?
0
declare -p PI
bash: declare: PI: not found
# now with read only variable:
declare -r PI=$(bc -l <<<'4*a(1)')
declare -p PI
declare -r PI="3.14159265358979323844"
echo ${PI@a} # flags
r
unset PI
bash: unset: PI: cannot unset: readonly variable
destroy PI
echo $?
0
declare -p PI
bash: declare: PI: not found
# and with non existant variable
destroy PI
echo $?
255
GDBの使用は非常に遅いです。代わりにctypes.shを試してください。 libffiを使用してbashのunbind_variable()を直接呼び出すことで機能します。これは、他のbashビルトインを使用するのとまったく同じくらい高速です。
$ readonly PI=3.14
$ unset PI
bash: unset: PI: cannot unset: readonly variable
$ source ctypes.sh
$ dlcall unbind_variable string:PI
$ declare -p PI
bash: declare: PI: not found
まず、ctypes.shをインストールする必要があります。
$ git clone https://github.com/taviso/ctypes.sh.git
$ cd ctypes.sh
$ ./autogen.sh
$ ./configure
$ make
$ Sudo make install
完全な説明とドキュメントについては、 https://github.com/taviso/ctypes.sh を参照してください。
不思議なことに、はい、これにより、bash内の関数、bashにリンクされたライブラリーの関数、または必要に応じて動的にロードされる外部ライブラリーを呼び出すことができます。 Bashは今やPerlと同じくらい危険です... ;-)
具体的には、TMOUT変数に書き込みます。 gdbが使用できない場合の別のオプションは、bashをホームディレクトリにコピーし、バイナリ内のTMOUT文字列を他の何か、たとえばXMOUXにパッチすることです。そして、この追加のシェルレイヤーを実行すると、タイムアウトになりません。
readonlyコマンドは、シェルプロセスが終了するまで、最終的かつ永続的にします。変数を変更する必要がある場合は、読み取り専用としてマークしないでください。
いいえ、現在のシェルにはありません。新しい値を割り当てたい場合は、新しい意味を持ち、read only
と見なされない新しいシェルをフォークする必要があります。
$ { ( readonly pi=3.14; echo $pi ); pi=400; echo $pi; unset pi; echo [$pi]; }
3.14
400
[]
Zshでは、
$ typeset +r PI
(はい、質問にbashと書かれていますが、zshでGoogleを検索すると、bashの質問もたくさんあります。)
unset
のマニュアルページからはできません。
名前ごとに、対応する変数または関数を削除します。オプションが指定されていない場合、または-vオプションが指定されている場合、各名前はシェル変数を参照します。 読み取り専用変数は設定解除できません。 -fが指定されている場合、各名前はシェル関数を参照し、関数定義は削除されます。設定されていない変数または関数はそれぞれ、後続のコマンドに渡される環境から削除されます。 RANDOM、SECONDS、LINENO、HISTCMD、FUNCNAME、GROUPS、またはDIRSTACKのいずれかが設定されていない場合、それらはその後リセットされても特別なプロパティを失います。名前が読み取り専用でない限り、終了ステータスはtrueです。
Bashで読み取り専用変数を「設定解除」するもう1つの方法は、使い捨てコンテキストでその変数を読み取り専用として宣言することです。
foo(){ declare -r PI=3.14; baz; }
bar(){ local PI=3.14; baz; }
baz(){ PI=3.1415927; echo PI=$PI; }
foo;
bash:PI:読み取り専用変数
bar;
PI = 3.1415927
これは、おそらく元の作者の意図であるスコープ内の「設定解除」ではありませんが、baz()の観点から変数を読み取り専用に設定し、後でそれを読み書き可能にしますbaz()のビュー、あなたはいくつかの先見の明であなたのスクリプトを書く必要があります。
$ PI=3.17
$ export PI
$ readonly PI
$ echo $PI
3.17
$ PI=3.14
-bash: PI: readonly variable
$ echo $PI
3.17
今何をする?
$ exec $BASH
$ echo $PI
3.17
$ PI=3.14
$ echo $PI
3.14
$
サブシェルは親の変数を継承できますが、保護されたステータスは継承しません。