bash 在bash中取消设置只读变量

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17397069/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 23:54:49  来源:igfitidea点击:

Unset readonly variable in bash

bashunset

提问by Kokizzu

How do I unset a readonly variable in Bash?

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

$ readonly PI=3.14

$ unset PI
bash: PI: readonly variable

or is it not possible?

还是不可能?

回答by anishsane

Actually, you can unset a readonly variable. but I must warn that this is a hacky method. Adding this answer, only as information, not as a recommendation. Use it at your own risk. Tested on ubuntu 13.04, bash 4.2.45.

实际上,您可以取消设置只读变量。但我必须警告,这是一种骇人听闻的方法。添加此答案,仅作为信息,而不是作为建议。需要您自担风险使用它。在 ubuntu 13.04、bash 4.2.45 上测试。

This method involves knowing a bit of bash source code & it's inherited from thisanswer.

这种方法涉及了解一些 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

$

A oneliner answer is to use the batch mode and other commandline flags, as provided in F. Hauri's answer:

oneliner 的答案是使用批处理模式和其他命令行标志,如F. Hauri 的回答中提供

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

sudomay or may not be needed based on your kernel's ptrace_scope settings. Check the comments on vip9937's answer for more details.

sudo根据内核的 ptrace_scope 设置,可能需要也可能不需要。查看有关 vip9937 答案的评论以获取更多详细信息。

回答by vip9937

I tried the gdb hack above because I want to unset TMOUT (to disable auto-logout), but on the machine that has TMOUT set as read only, I'm not allowed to use sudo. But since I own the bash process, I don't need sudo. However, the syntax didn't quite work with the machine I'm on.

我尝试了上面的 gdb hack,因为我想取消设置 TMOUT(以禁用自动注销),但是在将 TMOUT 设置为只读的机器上,我不允许使用 sudo。但是由于我拥有 bash 进程,所以我不需要 sudo。但是,语法在我使用的机器上不太适用。

This did work, though (I put it in my .bashrc file):

不过,这确实有效(我把它放在我的 .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

回答by Kevin

According to the man page:

根据手册页:

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

If you have not yet exported the variable, you can use exec "$0" "$@"to restart your shell, of course you will lose all other un-exported variables as well. It seems if you start a new shell without exec, it loses its read-only property for that shell.

如果您还没有导出变量,您可以使用exec "$0" "$@"重新启动您的外壳,当然您也会丢失所有其他未导出的变量。似乎如果你在没有 的情况下启动一个新的 shell exec,它会失去该shell的只读属性。

回答by Wil

Using GDB is terribly slow. Try ctypes.sh instead. It works by using libffi to directly call bash's unbind_variable() instead, which is every bit as fast as using any other bash builtin:

使用 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

First you will need to install ctypes.sh:

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

$ git clone https://github.com/taviso/ctypes.sh.git
$ cd ctypes.sh
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install

See https://github.com/taviso/ctypes.shfor a full description and docs.

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

For the curious, yes this lets you call any function within bash, or any function in any library linked to bash, or even any external dynamically-loaded library if you like. Bash is now every bit as dangerous as perl... ;-)

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

回答by F. Hauri

Shortly: inspired by anishsane's answer

很快:灵感来自anishsane 的回答

But with simplier syntax:

但使用更简单的语法:

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

With some improvement, as a function:

随着一些改进,作为一个功能:

My destroyfunction:

我的destroy功能:

Or How to play with variable meta data. Note usage of rare bashisms: local -n VARIABLE=$1and ${VARIABLE@a}...

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

destroy () { 
    local -n variable=
    declare -p  &>/dev/null || return -1 # Return if variable not exist
    local reslne result flags=${variable@a}
    [ -z "$flags" ] || [ "${flags//*r*}" ] && { 
        unset     # 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("''")' --pid=$$ --batch
    )
    return $result
}

You could copy this to a bash source filecalled destroy.bash, for sample...

您可以将其复制到名为的bash 源文件中destroy.bash,例如...

Explanation:

解释:

 1  destroy () { 
 2      local -n variable=
 3      declare -p  &>/dev/null || return -1 # Return if variable not exist
 4      local reslne result flags=${variable@a}
 5      [ -z "$flags" ] || [ "${flags//*r*}" ] && { 
 6          unset     # 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("''")' --pid=$$ --batch
14      )
15      return $result
16  }
 1  destroy () { 
 2      local -n variable=
 3      declare -p  &>/dev/null || return -1 # Return if variable not exist
 4      local reslne result flags=${variable@a}
 5      [ -z "$flags" ] || [ "${flags//*r*}" ] && { 
 6          unset     # 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("''")' --pid=$$ --batch
14      )
15      return $result
16  }
  • line 2 create a local referenceto submited variable.
  • line 3 prevent running on non existant variable
  • line 4 store parameter's attributes (meta) into $flags.
  • lines 5 to 8 will run unsetinstead of gdbif readonly flagnot present
  • lines 9 to 12 while read ... result= ... doneget return code of call unbindin gdboutput
  • line 13 gdbsyntax with use of --pidand --ex(see gdb --help).
  • line 15 return $resultof call unbindcommand.
  • 第 2 行创建提交变量的本地引用
  • 第 3 行防止在不存在的变量上运行
  • 第 4 行将参数的属性(元)存储到$flags.
  • 第 5 到 8 行将运行unset而不是gdb如果只读标志不存在
  • 第 9 到 12 行while read ... result= ... done获取call unbindingdb输出的返回码
  • gdb使用--pidand 的第 13 行语法--ex(参见gdb --help)。
  • 第15行返回$resultcall unbind命令。

In use:

正在使用:

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

回答by user1089933

Specifically wrt to the TMOUT variable. Another option if gdb is not available is to copy bash to your home directory and patch the TMOUT string in the binary to something else, for instance XMOUX. And then run this extra layer of shell and you will not be timed out.

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

回答by Radon Rosborough

In zsh,

在 zsh 中,

% typeset +r PI
% unset PI

(Yes, I know the question says bash. But when you Google for zsh, you also get a bunch of bash questions.)

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

回答by jaypal singh

No, not in the current shell. If you wish to assign a new value to it, you will have to fork a new shell where it will have a new meaning and will not be considered as read only.

不,不在当前的 shell 中。如果你想给它分配一个新的值,你将不得不分叉一个新的 shell,它会有一个新的含义并且不会被视为read only.

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

回答by Amit Verma

readonly command makes it final and permanent until the shell process terminates. If you need to change a variable, don't mark it readonly.

readonly 命令使其成为最终和永久的,直到 shell 进程终止。如果您需要更改变量,请不要将其标记为只读。

回答by Yu Hao

You can't, from manual page of unset:

你不能,从手册页unset

For each name, remove the corresponding variable or function. If no options are supplied, or the -v option is given, each name refers to a shell variable. Read-only variables may not be unset.If -f is specifed, each name refers to a shell function, and the function definition is removed. Each unset variable or function is removed from the environment passed to subsequent commands. If any of RANDOM, SECONDS, LINENO, HISTCMD, FUNCNAME, GROUPS, or DIRSTACK are unset, they lose their special properties, even if they are subsequently reset. The exit status is true unless a name is readonly.

对于每个名称,删除相应的变量或函数。如果未提供任何选项,或提供了 -v 选项,则每个名称都引用一个 shell 变量。 只读变量不能取消设置。如果指定了 -f,则每个名称都引用一个 shell 函数,并删除函数定义。每个未设置的变量或函数都会从传递给后续命令的环境中删除。如果 RANDOM、SECONDS、LINENO、HISTCMD、FUNCNAME、GROUPS 或 DIRSTACK 中的任何一个未设置,它们将失去其特殊属性,即使它们随后被重置。除非名称为只读,否则退出状态为真。