Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Get stack trace in Bash shell script/program.
# LICENSE: MIT, wtfpl or whatever OSS license you like
function get_stack () {
STACK=""
local i message="${1:-""}"
local stack_size=${#FUNCNAME[@]}
# to avoid noise we start with 1 to skip the get_stack function
for (( i=1; i<$stack_size; i++ )); do
local func="${FUNCNAME[$i]}"
[ x$func = x ] && func=MAIN
local linen="${BASH_LINENO[$(( i - 1 ))]}"
local src="${BASH_SOURCE[$i]}"
[ x"$src" = x ] && src=non_file_source
STACK+=$'\n'" at: "$func" "$src" "$linen
done
STACK="${message}${STACK}"
}
@akostadinov
Copy link
Author

akostadinov commented Sep 20, 2016

So first parameter of the function is an error message to be added to the stack trace. btw if your script supplied on bash's stdin (a bad idea in most cases), then the first position would be lost. If needed, then in the for loop, change it to i<$stack_size + 1. But as I said, it is not a good idea to feed your script to bash`s stdin, here's why.

-- originally posted as a StackOverflow answer

@matthewpersico
Copy link

matthewpersico commented Jan 27, 2022

Terrific! I was just about to write my own.

I use shellcheck on all bash scripts. I added

# shellcheck shell=bash

as the first line of the script and put it into https://www.shellcheck.net/ . Here are the results in case you are interested:

Line 8:
   for (( i=1; i<$stack_size; i++ )); do
                 ^-- SC2004 (style): $/${} is unnecessary on arithmetic variables.
 
Line 10:
      [ x$func = x ] && func=MAIN
        ^-- SC2268 (style): Avoid x-prefix in comparisons as it no longer serves a purpose.
         ^-- SC2086 (info): Double quote to prevent globbing and word splitting.

Did you mean: 
      [ $func = "" ] && func=MAIN
 
Line 13:
      [ x"$src" = x ] && src=non_file_source
        ^-- SC2268 (style): Avoid x-prefix in comparisons as it no longer serves a purpose.

Did you mean:
      [ "$src" = "" ] && src=non_file_source
 
Line 15:
      STACK+=$'\n'"   at: "$func" "$src" "$linen
                           ^-- SC2027 (warning): The surrounding quotes actually unquote this. Remove or escape them.
                                   ^-- SC2027 (warning): The surrounding quotes actually unquote this. Remove or escape them.

If one accepts shellcheck's advice, the result is:

# shellcheck shell=bash
# LICENSE: MIT, wtfpl or whatever OSS license you like
function get_stack () {
   STACK=""
   local i message="${1:-""}"
   local stack_size=${#FUNCNAME[@]}
   # to avoid noise we start with 1 to skip the get_stack function
   for (( i=1; i<stack_size; i++ )); do
      local func="${FUNCNAME[$i]}"
      [[ $func = "" ]] && func=MAIN
      local linen="${BASH_LINENO[$(( i - 1 ))]}"
      local src="${BASH_SOURCE[$i]}"
      [[ "$src" = "" ]] && src=non_file_source

      STACK+=$'\n'"   at: $func $src "$linen
   done
   STACK="${message}${STACK}"
}

I am wondering if that last modified line should read:

STACK+=$'\n'"   at: $func $src $linen"

@akostadinov
Copy link
Author

akostadinov commented Jan 28, 2022

@matthewpersico , that's awesome, the two lines Did you mean are not relevant. Last line about quotes - yeah, overusing the quotes. They are needed only to preserve indentation before at. I'll check the result later and update the gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment