如何在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)
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)
在 zsh 中,
% typeset +r PI
% unset PI
Run Code Online (Sandbox Code Playgroud)
(是的,我知道这个问题说的是 bash。但是当你谷歌搜索 zsh 时,你也会得到一堆 bash 问题。)
根据手册页:
unset [-fv] [name ...]
... Read-only variables may not be
unset. ...
Run Code Online (Sandbox Code Playgroud)
如果尚未导出变量,则可以使用exec "$0" "$@"
重新启动外壳程序,当然,您还将丢失所有其他未导出的变量。似乎,如果您启动一个不带的新外壳程序exec
,它将失去该外壳程序的只读属性。
使用 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 一样危险...... ;-)
小智 5
特别针对 TMOUT 变量。如果 gdb 不可用,另一个选择是将 bash 复制到您的主目录,并将二进制文件中的 TMOUT 字符串修补为其他内容,例如 XMOUX。然后运行这层额外的 shell,就不会超时了。
但是使用更简单的语法:
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
,作为示例...
Run Code Online (Sandbox Code Playgroud)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
如果不存在readonly标志while read ... result= ... done
的获取返回代码call unbind
的gdb
输出gdb
使用--pid
和的语法--ex
(请参阅参考资料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
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
44921 次 |
最近记录: |