90

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

$ readonly PI=3.14

$ unset PI
bash: PI: readonly variable

またはそれは不可能ですか?

4

15 に答える 15

119

実際には、読み取り専用変数の設定を解除できます。しかし、これはハックな方法であることを警告しなければなりません。この回答を推奨事項としてではなく、情報としてのみ追加します。ご自身の責任で使用してください。ubuntu 13.04、bash 4.2.45 でテスト済み。

この方法には、少しのbashソースコードを知ることが含まれ、この回答から継承されています。

$ 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

$

ワンライナーの答えは、 F. Hauriの答えで提供されているように、バッチモードとその他のコマンドラインフラグを使用することです:

$ sudo gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch

sudoカーネルの ptrace_scope 設定に基づいて、必要な場合とそうでない場合があります。詳細については、vip9937 の回答のコメントを確認してください。

于 2013-07-01T05:36:08.893 に答える
52

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
于 2014-01-22T21:41:15.293 に答える
8

手短に: anissaneの答えに触発されました

編集 2021-11-10:結果(int)に追加cast unbind_variable

ただし、より単純な構文を使用すると、次のようになります。

$ gdb -ex 'call (int) unbind_variable("PI")' --pid=$$ --batch

関数として、いくつかの改善を加えて:

私のdestroy機能:

または可変メタデータで遊ぶ方法。まれなbashismsの使用法に注意してください:local -n VARIABLE=$1そして${VARIABLE@a}...

destroy () { 
    declare -p $1 &>/dev/null || return -1 # Return if variable not exist
    local -n variable=$1
    local reslne result flags=${variable@a}
    [ -z "$flags" ] || [ "${flags//*r*}" ] && { 
        unset $1    # Don't run gdb if variable is not readonly.
        return $?
    }
    while read -r resline; do
        [ "$resline" ] && [ -z "${resline%%\$1 = *}" ] &&
            result=${resline##*1 = }
    done < <(
        exec gdb 2>&1 -ex 'call (int) unbind_variable("'$1'")' --pid=$$ --batch
    )
    return $result
}

サンプルとして、これをというbash ソース ファイルにコピーできます。destroy.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 (int) unbind_variable("'$1'")' --pid=$$ --batch
14      )
15      return $result
16  }
  • 2行目は、送信された変数へのローカル参照を作成します。
  • 3行目は、存在しない変数での実行を防ぎます
  • 4 行目は、パラメータの属性 (メタ) を に格納します$flags
  • 読み取り専用フラグが存在しない場合unsetの代わりに、5 行目から 8 行目が実行されます。gdb
  • 9 行目から 12 行目は、出力while read ... result= ... doneのリターン コードを取得します。call (int) unbind_variable()gdb
  • と を使用した 13 行gdb目の構文( を参照)。--pid--exgdb --help
  • $result15 行目でコマンドを返しunbind_variable()ます。

使用中で:

$ . destroy.bash
  • 任意の通常の (読み書き可能) 変数を使用した 1 つ目:

    $ 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
    
  • 読み取り専用変数を使用した2番目:

    $ 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
    
  • 存在しない変数を持つ3番目:

    $ destroy PI
    $ echo $?
    255
    
于 2018-11-24T13:02:02.937 に答える
6

マニュアルページによると:

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

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

于 2013-07-01T03:38:21.627 に答える
4

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

于 2015-01-20T00:29:56.683 に答える
3

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

于 2013-07-01T03:36:02.977 に答える
2

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

$ { ( readonly pi=3.14; echo $pi ); pi=400; echo $pi; unset pi; echo [$pi]; }
3.14
400
[]
于 2013-07-01T03:43:50.427 に答える
1

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

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

于 2013-07-01T03:38:03.793 に答える