在bash中取消设置readonly变量

Kok*_*zzu 72 bash unset

如何在Bash中取消设置readonly变量?

$ readonly PI=3.14

$ unset PI
bash: PI: readonly variable
Run Code Online (Sandbox Code Playgroud)

还是不可能?

ani*_*ane 100

实际上,您可以取消设置只读变量.但我必须警告,这是一种黑客方法.添加此答案,仅作为信息,而不是建议.使用风险由您自己承担.在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

$
Run Code Online (Sandbox Code Playgroud)

  • 现在这就是我所说的redneck bash编程;) (41认同)
  • 注意:不要试图改变`cat << EOF | sudo gdb`到`sudo gdb << EOF`.它_may_**不能正常工作,因为重定向的输入提供程序 - "bash"由于`gdb`附件而被停止. (3认同)
  • 我喜欢一个班轮:`echo -e "attach $$\n call unbind_variable(\"PI\")\n detach" | gdb` (2认同)

vip*_*937 45

我尝试了上面的gdb hack,因为我想取消设置TMOUT(禁用自动注销),但是在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
Run Code Online (Sandbox Code Playgroud)

  • 我建议使用`-q -n`选项来静音`gdb`并且不会为每个安全加载任何_.gdbinit_文件. (3认同)
  • *"因为我拥有bash进程,所以我不需要sudo"*请注意,这取决于您使用的操作系统及其配置方式.[对于大多数当前使用的Linux内核版本](https://www.kernel.org/doc/Documentation/security/Yama.txt),这是[通过`/ proc/sys/kernel/yama/ptrace_scope`控制] (https://askubuntu.com/q/41629/22949).最常见的值是`0`,在这种情况下你可以这样做,和`1`,在这种情况下[你可能不能作为`gdb`不是被调试的'bash`进程的直接父级]( https://unix.stackexchange.com/a/403693/11938). (3认同)

Rad*_*ugh 7

在 zsh 中,

% typeset +r PI
% unset PI
Run Code Online (Sandbox Code Playgroud)

(是的,我知道这个问题说的是 bash。但是当你谷歌搜索 zsh 时,你也会得到一堆 bash 问题。)


Kev*_*vin 6

根据手册页:

   unset [-fv] [name ...]
          ...   Read-only  variables  may  not  be
          unset. ...
Run Code Online (Sandbox Code Playgroud)

如果尚未导出变量,则可以使用exec "$0" "$@"重新启动外壳程序,当然,您还将丢失所有其他未导出的变量。似乎,如果您启动一个不带的新外壳程序exec,它将失去该外壳程序的只读属性。


Wil*_*Wil 6

使用 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
Run Code Online (Sandbox Code Playgroud)

首先,您需要安装 ctypes.sh:

$ git clone https://github.com/taviso/ctypes.sh.git
$ cd ctypes.sh
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
Run Code Online (Sandbox Code Playgroud)

有关完整说明和文档,请参阅https://github.com/taviso/ctypes.sh

出于好奇,是的,这使您可以调用 bash 中的任何函数,或任何链接到 bash 的库中的任何函数,甚至任何外部动态加载的库(如果您愿意)。Bash 现在和 perl 一样危险...... ;-)

  • 我假设你说的“include ctypes.sh”是指“source ctypes.sh”或“。ctypes.sh`。 (2认同)

小智 5

特别针对 TMOUT 变量。如果 gdb 不可用,另一个选择是将 bash 复制到您的主目录,并将二进制文件中的 TMOUT 字符串修补为其他内容,例如 XMOUX。然后运行这层额外的 shell,就不会超时了。

  • 甚至比 gdb hack 还要邪恶。所以...+1! (2认同)

F. *_*uri 5

简短说明:受奥尼沙尼(Anishsane)的回答启发

但是使用更简单的语法:

gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
Run Code Online (Sandbox Code Playgroud)

经过改进的功能:

我的destroy功能:

如何使用可变的元数据。注意罕见bashisms的用法:local -n VARIABLE=$1${VARIABLE@a}...

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
}
Run Code Online (Sandbox Code Playgroud)

您可以将其复制到名为的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 unbind_variable("'$1'")' --pid=$$ --batch
14      )
15      return $result
16  }
Run Code Online (Sandbox Code Playgroud)
  • 第2行创建提交变量的本地引用
  • 第3行防止在不存在的变量上运行
  • 第4行将参数的属性(meta)存储到中$flags
  • 第5至8行将运行,unset而不是gdb如果不存在readonly标志
  • 线9至12 while read ... result= ... done的获取返回代码call unbindgdb输出
  • 第13行gdb使用--pid和的语法--ex(请参阅参考资料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
Run Code Online (Sandbox Code Playgroud)