Re: misleading error message from variable modifier

2018-03-08 Thread Chet Ramey
On 3/7/18 1:00 PM, don fong wrote:
> Chet Ramey wrote:
>> What are the most important features that you consider to lack unit tests?
> 
> are there any tests that cover the variable modifiers, either unit tests or
> functional tests?

Yes. The test framework and tests are available for you to see. They're all
tests of shell behavior, since what concerns most people is shell behavior.
If you'd like to augment the test suite where you feel it lacks something,
please feel free to do so. I'm not interested in adding a new testing
framework.

-- 
``The lyf so short, the craft so long to lerne.'' - Chaucer
 ``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRUc...@case.eduhttp://tiswww.cwru.edu/~chet/



Unexpected behavior of 'declare +n var' when var's target is unset and undeclared

2018-03-08 Thread GreatBigDot
Configuration Information [Automatically generated, do not change]:
Machine: x86_64
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS:  -DPROGRAM='bash' -DCONF_HOSTTYPE='x86_64'
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='x86_64-unknown-linux-gnu'
-DCONF_VENDOR='unknown' -DLOCALEDIR='/usr/share/locale'
-DPACKAGE='bash' -DSHELL -DHAVE_CONFIG_H   -I.  -I. -I./include
-I./lib  -D_FORTIFY_SOURCE=2 -march=x86-64 -mtune=generic -O2 -pipe
-fstack-protector-strong -fno-plt
-DDEFAULT_PATH_VALUE='/usr/local/sbin:/usr/local/bin:/usr/bin'
-DSTANDARD_UTILS_PATH='/usr/bin' -DSYS_BASHRC='/etc/bash.bashrc'
-DSYS_BASH_LOGOUT='/etc/bash.bash_logout'
-DNON_INTERACTIVE_LOGIN_SHELLS -Wno-parentheses -Wno-format-security
uname output: Linux ArchBox0 4.15.7-1-ARCH #1 SMP PREEMPT Wed Feb 28
19:01:57 UTC 2018 x86_64 GNU/Linux
Machine Type: x86_64-unknown-linux-gnu

Bash Version: 4.4
Patch Level: 19
Release Status: release

Description:
If you try to turn a nameref back a regular variable with
'declare -n' but the nameref is pointing to an undeclared variable, the
nameref remains a nameref, and the variable it points to is declared
(but remians unset). This seems to contradict the bash manual, which
says that "all references, assignments, and attribute modifications to
[a nameref], **except those using or changing the -n attribute
itself,** are performed on the variable referenced by name's value."
[Emphasis mine.] Yet, if the nameref's target doesn't exist, then it is
still treated as a reference when dealing with the '-n' attribute.

Repeat-By:
Create a nameref 'foo' pointing to 'bar', where bar is unset
and undeclared:

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

Now attempt to turn 'foo' back into a regular variable:

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

This is not what happens when `bar` is set (or even, bizarrely enough,
when `bar` is unset but declared--see below). Observe:

 $ declare -n foo=bar
 $ bar='Hello World!'
 $ declare -p
 declare -n foo="bar"
 declare -- bar="Hello World!"

 $ declare +n foo
 $ declare -p
 declare -- foo="bar"
 declare -- bar="Hello World!"

The oddest part is that the normal behavior works when `bar` is unset,
but declared:

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

 `bar` truly is unset at this point, though it /isn't/
undeclared:

 $ declare -p bar
 declare -- bar

 $ set -u; printf '%s\n' "$bar"; set +u
 -bash: bar: unbound variable

 $ test -v bar && printf '%s\n' 'set' || printf '%s\n' 'unset'
 unset

 $ printf '%s\n' "${bar-0}" "${bar-1}"
 0
 1

 $ printf '<%s>\n' "${bar+0}"
 <>

 Anyway, with this setup, `declare +n` works like it usually
does:

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

$ declare +n foo; declare -p
declare foo="bar"
declare bar

I find this truly bizarre, as I didn't realize there was any
significance to a variable being /declared/; that is, I thought bash
thought there to be no significant difference between an unset variable
that was nevertheless declared with `declare`, and an unset, undeclared
variable. Yet the former behaves normally with respect to namerefs,
while the latter does not.

Is this expected behavior? If so, would anyone mind pointing me to
where this is documented and if there are any other edge cases related
to this? Thanks!

Fix:
When I posted this to StackOverflow [https://stackoverflow.com/
questions/49179596/making-a-nameref-a-regular-variable-in-bash], a user
answered that one can simply use a new option to unset:

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

$ unset -n foo; declare -p

This works regardless of the status of bar.

Trying to figure out how to unset a nameref (not the variable
it points to, but the variable itself) was what led me to this behavior
in the first place, and I can't think of any other common use case for
`declare +n` off the top of my head. Yet if that really was one's end
goal, it's easy enough to use the above fix:

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

$ _tmp="${!foo}"; declare -p
declare -n foo="bar"
declare -- _tmp="bar"

$ unset -n foo; declare -p
declare -- _tmp="bar"

$ foo="${_tmp}"; unset _tmp; declare -p
declare -- foo="bar"