I have very strange issues using bash
and exported function to give me a reliable answer to the call of the builtin caller
.
Here's my setup to illustrate this issue: Bash script bar
defines and exports function bar1
and bar2
. bar2
calls bar1
. Bash script bar
then execute bash script foo
which will call bar1
.
The caller builtin will then break only after the call of bar1
. Is this normal ? can you explain why ? can you simplify the following code that exposes the issue ?
To be perfectly clear, here's how to reproduce on your system: Build both file:
cd /tmp
cat <<"EOF" > foo
#!/bin/bash
bar1
EOF
chmod +x foo
cat <<"EOF" > bar
#!/bin/bash
bar2() {
echo "$FUNCNAME IN: $(caller 0)" >&2
}
export -f bar2
bar1() {
echo "$FUNCNAME BEFORE: $(caller 0)" >&2
bar2
echo "$FUNCNAME AFTER: $(caller 0)" >&2
}
export -f bar1
./foo
EOF
chmod +x bar
You can then fiddle and see:
$ ./bar
bar1 BEFORE: 3 main ./foo
bar2 IN:
bar1 AFTER:
I expected (with acceptable variations on the line numbers):
$ ./bar
bar1 BEFORE: 9 main ./foo
bar2 IN: 5 bar ./foo
bar1 AFTER: 9 main ./foo
Ultimately, my question would be: how could I circumvent this issue and get the caller in all cases ?
ADDITIONAL INFO:
4.3.42(1)-release (x86_64-pc-linux-gnu)
from ubuntu package 4.3-14ubuntu1
.This is a bug in bash. It was fixed in version 4.4.
In the presence of exported functions, the BASH_SOURCE
variable is not properly maintained. You can check it by displaying the contents of the FUNCNAME
, BASH_SOURCE
, BASH_LINENO
special variables:
cd /tmp
cat <<"EOF" > foo
#!/bin/bash
bar1
EOF
chmod +x foo
cat <<"EOF" > bar
#!/bin/bash
bar2() {
echo "$FUNCNAME IN: $(caller 0) [${FUNCNAME[@]}] [${BASH_SOURCE[@]}] [${BASH_LINENO[@]}]" >&2
}
export -f bar2
bar1() {
echo "$FUNCNAME BEFORE: $(caller 0) [${FUNCNAME[@]}] [${BASH_SOURCE[@]}] [${BASH_LINENO[@]}]" >&2
bar2
echo "$FUNCNAME AFTER: $(caller 0) [${FUNCNAME[@]}] [${BASH_SOURCE[@]}] [${BASH_LINENO[@]}]" >&2
}
export -f bar1
./foo
EOF
chmod +x bar
Output of ./bar
:
bar1 BEFORE: 3 main ./foo [bar1 main] [./foo] [3 0]
bar2 IN: [bar2 bar1 main] [./foo] [1 3 0]
bar1 AFTER: [bar1 main] [] [3 0]
As you can see, stack frames corresponding to invocations of exported functions aren't added to BASH_SOURCE
, but whenever a function returns the topmost stack frame is popped.
Note that the FUNCNAME
variable is not affected by this bug. Thus, if you need only the name of the caller you can obtain it as ${FUNCNAME[1]}
.