Unset readonly variable in bash

2019-01-08 06:17发布

how to unset readonly variable in Bash?

$ readonly PI=3.14

$ unset PI
bash: PI: readonly variable

or is it not possible?

标签: bash unset
11条回答
小情绪 Triste *
2楼-- · 2019-01-08 06:34

Shortly: inspired by anishsane's answer

But with simplier syntax:

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

My destroy function:

Or How to check variable meta data...

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
}

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

Explanation:

 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 < <( gdb 2>&1 -ex 'call unbind_variable("'$1'")' --pid=$$ --batch )
13      return $result
14  }
  • line 2 create a reference to submited variable used for meta datas
  • line 3 prevent running on non existant variable
  • lines 5 to 8 will run unset instead of gdb if readonly flag not present
  • while read ... result= ... done get result of call in gdb output
  • gdb syntax with use of --pid and --ex (see gdb --help).

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
查看更多
我只想做你的唯一
3楼-- · 2019-01-08 06:35

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:

$ 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:

$ 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.sh for a full description and docs.

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... ;-)

查看更多
\"骚年 ilove
4楼-- · 2019-01-08 06:39

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.

查看更多
狗以群分
5楼-- · 2019-01-08 06:40

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 recommendation. Use at your own risk. Tested on ubuntu 13.04, bash 4.2.45.

This method involves knowing a bit of bash source code & it's inherited from 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

$
查看更多
Fickle 薄情
6楼-- · 2019-01-08 06:42

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.

$ { ( readonly pi=3.14; echo $pi ); pi=400; echo $pi; unset pi; echo [$pi]; }
3.14
400
[]
查看更多
祖国的老花朵
7楼-- · 2019-01-08 06:43

One other way to "unset" a read-only variable in Bash is to declare that variable read-only in a disposable context:

foo(){ declare -r PI=3.14; baz; }
bar(){ local PI=3.14; baz; }

baz(){ PI=3.1415927; echo PI=$PI; }

foo;

bash: PI: readonly variable

bar; 

PI=3.1415927

While this is not "unsetting" within scope, which is probably the intent of the original author, this is definitely setting a variable read-only from the point of view of baz() and then later making it read-write from the point of view of baz(), you just need to write your script with some forethought.

查看更多
登录 后发表回答