Unset только для чтения переменная в bash

Как отключить переменную readonly в Bash?

$ readonly PI=3.14

$ unset PI
bash: PI: readonly variable

или это невозможно?

10 ответов


на самом деле вы можете сбросить переменной readonly. но я должен предупредить, что это хакерский метод. Добавление этого ответа, только как информации, а не как рекомендации. Используйте на свой страх и риск. Протестировано на 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

$

я попробовал взломать gdb выше, потому что я хочу отключить TMOUT (отключить автоматический выход), но на машине, у которой TMOUT установлен только для чтения, мне не разрешено использовать sudo. Но поскольку я владею процессом bash, мне не нужен sudo. Однако синтаксис не совсем работал с машиной, на которой я нахожусь.

это сработало, хотя (я положил его в мой .bashrc file):

# 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

В соответствии с man page:

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

Если вы еще не экспортировать переменную, вы можете использовать exec "" "$@" чтобы перезапустить оболочку, конечно, вы потеряете и все остальные неэкспортированные переменные. Кажется, если вы начинаете новую оболочку без exec, оно теряет свое свойство только для чтения для этой оболочки.


команда readonly делает ее окончательной и постоянной, пока процесс оболочки не завершится. Если вам нужно изменить переменную, не отмечайте ее только для чтения.


нет, не в текущей оболочке. Если вы хотите присвоить ему новое значение, вам придется раскошелиться на новую оболочку, где она будет иметь новое значение и не будет считаться read only.

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

использование GDB ужасно медленно. Вместо того, чтобы попробовать ctypes.sh . Он работает с помощью libffi для прямого вызова unbind_variable() bash, который так же быстр, как и любой другой bash builtin:

$ readonly PI=3.14
$ unset PI
bash: unset: PI: cannot unset: readonly variable

$ include ctypes.sh
$ dlcall unbind_variable string:PI

$ declare -p PI
bash: declare: PI: not found

сначала вам нужно будет установить ctypes.sh:

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

см.https://github.com/taviso/ctypes.sh для полного описания и документов.

для любопытных, да, это позволяет вызывать любую функцию в bash или любую функцию в любой библиотеке связанный с bash или даже любой внешней динамически загружаемой библиотекой, если хотите. Баш теперь так же опасен, как и Перл... ;-)


вы не можете, с ручной страницы unset:

для каждого имени удалите соответствующую переменную или функцию. Если параметры не указаны или указан параметр-v, то каждое имя ссылается на переменную оболочки. переменные только для чтения не могут быть отключены. если-f указано, каждое имя ссылается на функцию оболочки, и определение функции удаляется. Каждая переменная или функция unset удаляется из переданной среды к последующим командам. Если любой из RANDOM, SECONDS, LINENO, HISTCMD, FUNCNAME, GROUPS или DIRSTACK не установлен, они теряют свои специальные свойства, даже если они впоследствии сбрасываются. Состояние выхода true, если имя не только для чтения.


специально wrt для переменной TMOUT. Другой вариант, если gdb недоступен, - скопировать bash в ваш домашний каталог и исправить строку TMOUT в двоичном файле на что-то другое, например XMOUX. А затем запустите этот дополнительный слой оболочки, и вы не будете тайм-аут.


еще один способ "снять" переменную только для чтения в Bash-объявить эту переменную только для чтения в одноразовом контексте:

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

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

foo;

bash: PI: переменная только для чтения

bar; 

PI=3,1415927

хотя это не "расстраивает" в пределах области, что, вероятно, является намерением исходного автора, это, безусловно, установка переменной только для чтения с точки зрения baz (), а затем делает ее чтение-запись из с точки зрения baz(), вам просто нужно написать свой скрипт с некоторой предусмотрительностью.


в zsh,

$ typeset +r PI

(Да, я знаю, что вопрос говорит Баш. Но когда Вы Google для zsh, вы также получаете кучу вопросов bash.)