BASH_SOURCE
, BASH_LINENO
, and FUNCNAME
environment variables, set by Bash during script executions, to display a call-stack of one script to another, excluding function calls; this will show only the invocations of one script to another.showStack() { local i for (( i=1; i < ${#BASH_SOURCE[@]}; i++ )); do [ "${FUNCNAME[$i]}" = source ] && echo " ${BASH_SOURCE[$i]}:${BASH_LINENO[(($i - 1))]}" done }
Each of my startup dot-files echo their name when they start, so their call to showStack
might look like:
.bash_profile... .profile... /home/bill/.profile:31 /home/bill/.bash_profile:28 .bashrc... /home/bill/.bashrc:30 /home/bill/.profile:243 /home/bill/.bash_profile:28
The explicit output shows that .bash_profile
is started, followed by .profile
. In .profile
, is a call to showStack
which displays the script calls: .bash_profile
at line 28 invokes .profile
, which, at line 31, calls showStack
. Later, .bashrc
is invoked and it calls showStack
at line 30; it was invoked from .profile
at its line 243; which was invoked by .bash_profile
‘s line 28.
The three Bash variables, BASH_SOURCE
, BASH_LINENO
, and FUNCNAME
, are arrays whose corresponding elements describe the script files, their line numbers, and function names which make up the call-stack. If the invocation is not contained within a function, then the corresponding FUNCNAME
element is set to source
.
caller
Builtin
I was aware of the Bash variables and wanted the exercise of figuring them out; but if I’d queried StackOverflow on the topic, would have revealed other’s similar solutions; notably, one using Bash’s builtin command, caller
:
showStack() { while caller $i; do ((i++)) done }
Which, for the same scripts, would output (here, I did not filter for the source
entries and includes the function calls):
.bash_profile... .profile... 31 source /home/bill/.profile 21 _sourceFiles /home/bill/.bash_profile 25 _sourceDotFiles /home/bill/.bash_profile 30 source /home/bill/.bash_profile .bashrc... 30 source /home/bill/.bashrc 243 source /home/bill/.profile 21 _sourceFiles /home/bill/.bash_profile 25 _sourceDotFiles /home/bill/.bash_profile 30 source /home/bill/.bash_profile
These are well illustrated by @kyb in his utils.bash script:
function stacktrace { local i=1 while caller $i | read line func file; do echodbg "[$i] $file:$line $func()" ((i++)) done } function stacktrace2 { local i=${1:-1} size=${#BASH_SOURCE[@]} ((i<size)) && echodbg "STACKTRACE" for ((; i < size-1; i++)) ;do ## -1 to exclude main() ((frame=${#BASH_SOURCE[@]}-i-2 )) echodbg "[$frame] ${BASH_SOURCE[$i]:-}:${BASH_LINENO[$i]} ${FUNCNAME[$i+1]}()" done }