Discussion:
The builtin array variable 'FUNCNAME' is considered unset by many expansions, even when set.
Great Big Dot
2018-11-08 06:15:56 UTC
Permalink
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.18.16-arch1-1-ARCH #1 SMP PREEMPT Sat Oct 20
22:06:45 UTC 2018 x86_64 GNU/Linux
Machine Type: x86_64-unknown-linux-gnu

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

Description:
The builtin array variable FUNCNAME (which provides a way to trace the
stack of functions called so far) appears to be unset according to certain
bash expansions, even when it isn't. If the following code is saved to a
file and executed (this doesn't work at the command line), the problems
begin to appear:

printf -- '%q\n' "${FUNCNAME}"
printf -- '%q\n' "${FUNCNAME[0]}"
printf -- '%q\n' "${FUNCNAME[*]}"

This yields:

''
main
main

For some reason, FUNCNAME appears to be either empty or unset, though it is
supposed to default to FUNCNAME[0]. In fact, if you turn on `set -u`, the
first line of output becomes:

./script:1: FUNCNAME: unbound variable

So it's viewed as unset, not merely the empty string. Any other array
outputs 'main' on all three lines. But it gets worse:

printf -- '%s\n' "${FUNCNAME[*]}"
declare -p -- FUNCNAME

yields:

main
declare -a FUNCNAME

That is, `declare -p`---which I figured would be the canonical authority on
a variable and all its properties---gives a completely wrong answer.
Namely, it says that FUNCNAME is unset, albeit declared as a linear array.
It *should* output 'declare -a FUNCNAME=([0]="main")'.

As far as I can tell, FUNCNAME is the only variable that behaves this way;
in particular, the other builtin variables provided for stack tracing,
BASH_LINENO and BASH_SOURCE, behave exactly like every other array. In
fact, if you unset FUNCNAME (which, according to the manual, strips its
special properties) and reset it to `FUNCNAME=('main')`, the above (and
ensuing) anomalies do not occur.

The problems run much deeper than the above examples. When I tried to
figure out what was causing it, I only ran into more nonsensical behavior.
I ended up making a brief script that lists a bunch of different anomalous
behaviors in a (somewhat) organized format; a link to a "GitHub gist" is
below. Among other things, we have this inexplicable arbitrariness (again,
only works if saved to a file and executed):

printf -- '%q\n' "${FUNCNAME[*]}"
printf -- '%q\n' "${FUNCNAME[*]/x/x}"
printf -- '%q\n' "${FUNCNAME[*]#x}"

Yields:

main
main
''

That is, substring-replacement works, but substring removal doesn't,
despite the array clearly being set. On any other array, it works correctly
in both instances. It gets much weirder, and additional notes are in the
comments of the script---though I still have no clue what's causing this.

Repeat-By:

Save this script to a file, make it executable, and run it:

https://gist.github.com/greatBigDot/a3cd1a9febcf7a39a7e0ed6b6534fdde#file-bash-funcname-weirdness

The output should make it pretty clear what expansions are taking place,
but see the comments for descriptions of the expected behavior and further
discussion. In particular, there are lots of expansions that follow the
same general patterns as the ones included in the script, but left out for
conciseness; see the comments for those. (I will update the gist if any of
what I said turns out to be wrong.)

Fix:

It is possible to access the variable normally, but there are a ton of
random pitfalls. That being said, if you're careful to be conservative with
your expansions and explicit with your subscripts, and if you don't trust
`declare -p`, I think everything should go fine.
Greg Wooledge
2018-11-08 13:27:05 UTC
Permalink
Post by Great Big Dot
The builtin array variable FUNCNAME (which provides a way to trace the
stack of functions called so far) appears to be unset according to certain
bash expansions, even when it isn't. If the following code is saved to a
file and executed (this doesn't work at the command line), the problems
printf -- '%q\n' "${FUNCNAME}"
printf -- '%q\n' "${FUNCNAME[0]}"
printf -- '%q\n' "${FUNCNAME[*]}"
I don't see any functions there. From the manual:

FUNCNAME
[...]
This variable exists only when a shell function is executing.
Assignments to FUNCNAME have no effect. If FUNCNAME is unset,
it loses its special properties, even if it is subsequently
reset.

Since your example doesn't show a function being defined or called, I
would expect it to show only empty strings.

Also, as a side note, "${x}" and "${x[0]}" are always the same. Literally
always. Doesn't matter what type of variable x is (or isn't).
Great Big Dot
2018-11-08 15:19:55 UTC
Permalink
Post by Greg Wooledge
Since your example doesn't show a function being defined or called, I
would expect it to show only empty strings.
Oh yeah, right. For some reason I thought the part about "main" still
applied. But the actual expected behavior doesn't occur either;
"${FUNCNAME[0]}" and "${FUNCNAME[*]}" are both nonempty! I thought
"${FUNCNAME}" was the incorrect one since it alone was empty, but it turns
out that's the only one that's correct.
Post by Greg Wooledge
Also, as a side note, "${x}" and "${x[0]}" are always the same. Literally
always. Doesn't matter what type of variable x is (or isn't).
That's what I thought too, before finding this bug. When executing from a
file, "${FUNCNAME[0]}" exists but "${FUNCNAME}" doesn't. I incorrectly
thought the latter was wrong, but the inconsistency is still there!
Post by Greg Wooledge
Post by Great Big Dot
The builtin array variable FUNCNAME (which provides a way to trace the
stack of functions called so far) appears to be unset according to
certain
Post by Great Big Dot
bash expansions, even when it isn't. If the following code is saved to a
file and executed (this doesn't work at the command line), the problems
printf -- '%q\n' "${FUNCNAME}"
printf -- '%q\n' "${FUNCNAME[0]}"
printf -- '%q\n' "${FUNCNAME[*]}"
FUNCNAME
[...]
This variable exists only when a shell function is executing.
Assignments to FUNCNAME have no effect. If FUNCNAME is
unset,
it loses its special properties, even if it is subsequently
reset.
Since your example doesn't show a function being defined or called, I
would expect it to show only empty strings.
Also, as a side note, "${x}" and "${x[0]}" are always the same. Literally
always. Doesn't matter what type of variable x is (or isn't).
Chet Ramey
2018-11-08 14:57:06 UTC
Permalink
Post by Great Big Dot
Bash Version: 4.4
Patch Level: 23
Release Status: release
The builtin array variable FUNCNAME (which provides a way to trace the
stack of functions called so far) appears to be unset according to certain
bash expansions, even when it isn't. If the following code is saved to a
file and executed (this doesn't work at the command line), the problems
printf -- '%q\n' "${FUNCNAME}"
printf -- '%q\n' "${FUNCNAME[0]}"
printf -- '%q\n' "${FUNCNAME[*]}"
Thanks for the report. It should expand to the empty string in all these
cases.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Great Big Dot
2018-11-08 15:28:28 UTC
Permalink
It should expand to the empty string in all these cases.
Oh yeah, right, because a function isn't even running. Duh. All my comments
about expected behavior should be inverted, then, I guess. Out of
curiosity, do you have any idea what's causing bash to *sometimes*
correctly conclude that FUNCNAME[0] is empty and other times that
FUNCNAME[*] is empty, under seemingly arbitrary circumstances? I can't
figure out what could be causing that.
Post by Great Big Dot
Bash Version: 4.4
Patch Level: 23
Release Status: release
The builtin array variable FUNCNAME (which provides a way to trace the
stack of functions called so far) appears to be unset according to
certain
Post by Great Big Dot
bash expansions, even when it isn't. If the following code is saved to a
file and executed (this doesn't work at the command line), the problems
printf -- '%q\n' "${FUNCNAME}"
printf -- '%q\n' "${FUNCNAME[0]}"
printf -- '%q\n' "${FUNCNAME[*]}"
Thanks for the report. It should expand to the empty string in all these
cases.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey
2018-11-08 15:38:01 UTC
Permalink
Post by Great Big Dot
It should expand to the empty string in all these cases.
Oh yeah, right, because a function isn't even running. Duh. All my comments
about expected behavior should be inverted, then, I guess. Out of
curiosity, do you have any idea what's causing bash to *sometimes*
correctly conclude that FUNCNAME[0] is empty and other times that
FUNCNAME[*] is empty, under seemingly arbitrary circumstances? I can't
figure out what could be causing that.
Yes. The internal function that returns an array value isn't honoring the
`not visible' setting. That's where the change needs to be.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Loading...