Skip to content
shellhacks.dev
Debugging

Print a stack trace on error

Print the call chain on a failure with an ERR trap that walks the FUNCNAME, BASH_SOURCE and BASH_LINENO arrays, innermost function first.

1 min read
Summarize in AI

An error three functions deep reports only its own line, and you want the path the script took to get there.

Shell
set -eE
trap 'rc=$?; echo "error $rc in: $BASH_COMMAND" >&2
  for ((i = 0; i < ${#FUNCNAME[@]} - 1; i++)); do
    echo "  at ${FUNCNAME[i]} (called from ${BASH_SOURCE[i+1]}:${BASH_LINENO[i]})" >&2
  done' ERR
inner() { false; }
middle() { inner; }
outer() { middle; }
outer
Text
error 1 in: false
  at inner (called from main.sh:7)
  at middle (called from main.sh:8)
  at outer (called from main.sh:9)

Bash keeps the call stack in three parallel arrays: FUNCNAME has the function names with the innermost first, BASH_LINENO[i] is the line from which FUNCNAME[i] was called, and BASH_SOURCE[i+1] is the file that line is in. Inside an ERR trap those arrays still describe the failing context, so the loop prints one line per function frame. The arrays are documented in the Bash manual.

Define the trap before the functions that may fail, and keep -E so it also fires inside them. The trap can run more than once as the failure propagates through each frame under set -e. The arrays are empty for a command typed at the prompt and for bash -c, so BASH_SOURCE has no file name there. caller prints one frame at a time.

Share

More snippets

Debugging

Print each line as it is read

Print every script line as the shell reads it, before expansion, with set -v. Pair it with set -x to see the source line and the expanded command.

1 min read
Debugging

Trace only part of a script

Trace only the lines you are debugging by wrapping them in set -x and set +x. Write { set +x; } 2>/dev/null so the switch-off command is not traced itself.

1 min read

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.