Устанавливает переменную readonly в bash
как отключить переменную readonly в Bash?
$ readonly PI=3.14
$ unset PI
bash: PI: readonly variable
или это невозможно?
Ответы
Ответ 1
Собственно, вы можете отключить переменную 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
$
Ответ 2
Я попробовал взломать gdb выше, потому что я хочу отключить 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
Ответ 3
Согласно справочной странице:
unset [-fv] [name ...]
... Read-only variables may not be
unset. ...
Если вы еще не экспортировали переменную, вы можете использовать exec "$0" "[email protected]"
для перезапуска вашей оболочки, конечно, вы потеряете и все другие неэкспортированные переменные. Кажется, что если вы запускаете новую оболочку без exec
, она теряет свойство только для чтения для этой оболочки.
Ответ 4
Но с более простым синтаксисом:
gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
С некоторым улучшением, как функция:
Моя функция destroy
:
Или Как играть с переменными метаданными. Обратите внимание на использование редких ударов: local -n VARIABLE=$1
и ${[email protected]}
...
destroy () {
local -n variable=$1
declare -p $1 &>/dev/null || return -1 # Return if variable not exist
local reslne result flags=${[email protected]}
[ -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
}
Вы можете скопировать это в исходный файл 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=${[email protected]}
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 }
- строка 2 создает локальную ссылку на подзаголовок переменной.
- строка 3 запрещает работу с несуществующей переменной
- строка 4 сохраняет атрибуты параметров (мета) в
$flags
.
- строки с 5 по 8 будут запускать
unset
вместо gdb
, если флаг только для чтения отсутствует
- строки 9–12
while read ... result= ... done
получают код возврата call unbind
на выходе gdb
- строка 13
gdb
синтаксис с использованием --pid
и --ex
(см. gdb --help
).
- строка 15 возвращает
$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 ${[email protected]} # 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 ${[email protected]} # 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
Ответ 5
В частности, ответьте на переменную TMOUT. Другой вариант, если gdb недоступен, - это скопировать bash в ваш домашний каталог и исправить строку TMOUT в двоичном файле на что-то еще, например XMOUX. А затем запустите этот дополнительный слой оболочки, и вы не будете время ожидания.
Ответ 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
Сначала вам нужно установить 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, или даже любую внешнюю динамически загружаемую библиотеку, если хотите. Bash теперь так же опасен, как и Perl... ;-)
Ответ 7
Команда readonly делает ее окончательной и постоянной, пока процесс оболочки не завершится. Если вам нужно изменить переменную, не отмечайте ее только для чтения.
Ответ 8
Нет, не в текущей оболочке. Если вы хотите присвоить ему новое значение, вам придется разблокировать новую оболочку, где она будет иметь новое значение и не будет считаться read only
.
$ { ( readonly pi=3.14; echo $pi ); pi=400; echo $pi; unset pi; echo [$pi]; }
3.14
400
[]
Ответ 9
В zsh,
$ typeset +r PI
(Да, я знаю, что вопрос говорит bash. Но когда вы Google для zsh, вы также получаете кучу вопросов bash.)
Ответ 10
На странице руководства unset
вы не можете:
Для каждого имени удалите соответствующую переменную или функцию. Если параметры не заданы или не указана опция -v, каждое имя относится к переменной оболочки. Переменные только для чтения не могут быть отменены. Если задано -f, каждое имя относится к функции оболочки, а определение функции удаляется. Каждая незанятая переменная или функция удаляется из среды, переданной в последующие команды. Если любые из RANDOM, SECONDS, LINENO, HISTCMD, FUNCNAME, GROUPS или DIRSTACK не установлены, они теряют свои особые свойства, даже если они впоследствии reset. Статус выхода - true, если только имя не указано только.
Ответ 11
Еще один способ "сбросить" переменную, доступную только для чтения, в Bash - объявить эту переменную только для чтения в одноразовом контексте:
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
Хотя это и не "отключение" в области видимости, что, вероятно, и было целью первоначального автора, это определенно устанавливает переменную только для чтения с точки зрения baz(), а затем делает ее доступной для чтения-записи с точки зрения view baz(), вам просто нужно написать свой сценарий с некоторой продуманностью.
Ответ 12
$ PI=3.17
$ export PI
$ readonly PI
$ echo $PI
3.17
$ PI=3.14
-bash: PI: readonly variable
$ echo $PI
3.17
Что теперь делать?
$ exec $BASH
$ echo $PI
3.17
$ PI=3.14
$ echo $PI
3.14
$
Подоболочка может наследовать родительские переменные, но не наследует их защищенный статус.
Ответ 13
Альтернатива, если gdb недоступен: вы можете использовать команду enable
, чтобы загрузить пользовательскую встроенную функцию, которая позволит вам сбросить атрибут только для чтения. Суть кода, который делает это:
SETVARATTR (find_variable ("TMOUT"), att_readonly, 1);
Очевидно, вы бы заменили TMOUT
на переменную, которая вас интересует.
Если вы не хотите превращать это во встроенную функцию самостоятельно, я раздвоил bash в GitHub и добавил полностью написанную и готовую к компиляции загружаемую встроенную систему под названием readwrite
. Коммит находится на https://github.com/josephcsible/bash/commit/bcec716f4ca958e9c55a976050947d2327bcc195. Если вы хотите использовать его, возьмите исходный код Bash с моим коммитом, запустите ./configure && make loadables
, чтобы построить его, затем enable -f examples/loadables/readwrite readwrite
, чтобы добавить его в текущий сеанс, затем readwrite TMOUT
, чтобы использовать его.