web-dev-qa-db-ja.com

bashで読み取り専用変数の設定を解除します

bashで読み取り専用変数を設定解除するにはどうすればよいですか?

$ readonly PI=3.14

$ unset PI
bash: PI: readonly variable

それとも不可能ですか?

65
Kokizzu

実際には、読み取り専用変数の設定を解除できます。しかし、これはハッキーな方法であることを警告する必要があります。推奨事項ではなく、情報としてのみこの回答を追加します。自己責任。 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

$
92
anishsane

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
45
vip9937

Manページによると:

   unset [-fv] [name ...]
          ...   Read-only  variables  may  not  be
          unset. ...

変数をまだエクスポートしていない場合は、exec "$0" "$@"を使用してシェルを再起動できます。もちろん、エクスポートされていない他のすべての変数も失われます。 execなしで新しいシェルを起動すると、そのシェルの読み取り専用プロパティが失われるようです。

5
Kevin

まもなく: anishsane's answer に触発された

しかし、単純な構文では:

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  }
  • 2行目は、メタデータに使用される送信済み変数へのreferenceを作成します
  • 3行目は、存在しない変数で実行されないようにします
  • 4行目は、パラメーターの属性を$flags変数に格納します。
  • 5行目から8行目は、readonly flagが存在しない場合、unsetの代わりにgdbを実行します
  • 9行目から12行目while read ... result= ... donegdb出力のcall unbindの戻りコードを取得
  • --pidおよび--exを使用した13行目のgdb構文(gdb --helpを参照)。
  • 15行目は$resultcall 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
3
F. Hauri

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と同じくらい危険です... ;-)

2
Wil

具体的には、TMOUT変数に書き込みます。 gdbが使用できない場合の別のオプションは、bashをホームディレクトリにコピーし、バイナリ内のTMOUT文字列を他の何か、たとえばXMOUXにパッチすることです。そして、この追加のシェルレイヤーを実行すると、タイムアウトになりません。

2
user1089933

readonlyコマンドは、シェルプロセスが終了するまで、最終的かつ永続的にします。変数を変更する必要がある場合は、読み取り専用としてマークしないでください。

2
Amit

いいえ、現在のシェルにはありません。新しい値を割り当てたい場合は、新しい意味を持ち、read onlyと見なされない新しいシェルをフォークする必要があります。

$ { ( readonly pi=3.14; echo $pi ); pi=400; echo $pi; unset pi; echo [$pi]; }
3.14
400
[]
2
jaypal singh

Zshでは、

$ typeset +r PI

(はい、質問にbashと書かれていますが、zshでGoogleを検索すると、bashの質問もたくさんあります。)

2

unsetのマニュアルページからはできません。

名前ごとに、対応する変数または関数を削除します。オプションが指定されていない場合、または-vオプションが指定されている場合、各名前はシェル変数を参照します。 読み取り専用変数は設定解除できません。 -fが指定されている場合、各名前はシェル関数を参照し、関数定義は削除されます。設定されていない変数または関数はそれぞれ、後続のコマンドに渡される環境から削除されます。 RANDOM、SECONDS、LINENO、HISTCMD、FUNCNAME、GROUPS、またはDIRSTACKのいずれかが設定されていない場合、それらはその後リセットされても特別なプロパティを失います。名前が読み取り専用でない限り、終了ステータスはtrueです。

1
Yu Hao

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()のビュー、あなたはいくつかの先見の明であなたのスクリプトを書く必要があります。

1
Wil
$ 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
$

サブシェルは親の変数を継承できますが、保護されたステータスは継承しません。

0
jezzaaaa