Создание nameref регулярной переменной в bash

РЕДАКТИРОВАТЬ: Это было подтверждено как ошибка и будет исправлено: https://lists.gnu.org/archive/html/bug-bash/2018-03/msg00055.html


Так что я возиться с функцией косвенного обращения bash, namerefs. Я подумала, что поняла это, но потом наткнулась на то, что смутило меня, когда я пыталась понять, как сделать namerefs обычными переменными.

Вот соответствующий отрывок из man bash:

declare -n Присвойте каждому имени атрибут nameref, сделав ссылку на имя другой переменной. Эта другая переменная определяется значением name. Все ссылки, присвоения и модификации атрибутов name, кроме тех, которые используют или изменяют сам атрибут -n, выполняются в переменной, на которую ссылается значение name.


Часть я понимаю

Скажем, вы хотели сбросить nameref- не varaible, на который он указывает, а саму переменную. Вы не могли бы просто сказать unset foo потому что это на самом деле может привести к foo указывает на; вместо этого вы должны сделать его обычной переменной, а затем сбросить ее:

$ declare -p

$ foo=bar; bar='hello world'

$ declare -p
declare -- foo="bar"
declare -- bar="hello world"

$ declare -n foo; declare -p    # 'foo' is now a nameref
declare -n foo="bar"
declare -- bar="hello world"

$ declare +n foo; declare -p    # 'foo' is no longer a nameref
declare -- foo="bar"
declare -- bar="hello world"

$ unset foo; declare -p         # 'foo' is unset, not bar
declare -- bar="hello world"

Часть я не понимаю

Это все имеет смысл для меня и согласуется с моим прочтением приведенного выше руководства. Что меня смущает, так это то, что происходит при незначительном изменении в вышесказанном, а именно: bar неустановленный и необъявленный:

...
$ declare -p
declare -n foo="bar"

$ echo "${foo}"                 # These two commands behave as expected--i.e., identically to how namerefs usually behave, just with an unset variable.
-bash: foo: unbound variable

$ echo "${!foo}"
bar

$ declare +n foo; declare -p    # Should make 'foo' a regular variable, right? Nope.
declare -n foo="bar"            # Still a nameref--wtf?
declare -- bar                  # And now bar's back--unset still, but declared. Wtf??

$ declare +n foo; declare -p    # THIS, however, works like I thought it would--but *why*? In both cases 'bar' is unset...
declare -- foo="bar"
declare -- bar

Я, очевидно, неправильно понимаю, как namerefs должны работать. Исходя из отрывка из man, я бы подумал, что сброс атрибута nameref foo должен работать на foo независимо от того, является ли его целью, bar не объявлен.

Обратите внимание, что это работает так, как я думал, когда bar не установлен, но объявлен. Это самая странная часть для меня - я не осознавал, что было какое-то значение для незадекларированной переменной! test -v, ${var-_}, ${var+_}, а также set -u кажется, что все заботятся о том, установлена ​​ли переменная, и не делают различий между (A) неустановленной, необъявленной переменной и (B) неустановленной, объявленной переменной.

Может кто-нибудь объяснить, что здесь происходит, возможно, указать на ту часть руководства, которая объясняет это? Есть ли другие особые случаи в поведении имен, которые я собираюсь запутать? Спасибо!


Потенциально актуальная информация:

$ bash --version
GNU bash, version 4.4.19(1)-release (x86_64-unknown-linux-gnu)
...

$ echo "$-"
himuBCHs

Обратите внимание, что поведение сохраняется без set -u; Я просто сделал это, чтобы сообщения bash стали немного понятнее.

1 ответ

Решение

Там новый аргумент unset явно с целью отмены определения nameref (в отличие от переменной, на которую он указывает):

unset -n namevar
Другие вопросы по тегам