Подтвердить что ты не робот

Устанавливает переменную readonly в bash

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

$ readonly PI=3.14

$ unset PI
bash: PI: readonly variable

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

4b9b3361

Ответ 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, чтобы использовать его.