docwhat's avatardocwhat's blog

Tracebacks in bash

I don’t like to write programs in bash. It’s not a very pretty language. But it has one advantage over a lot of other languages:

It’s on your system. Every Unix-like system has /bin/bash; Redhat, Ubuntu, and even OS X.

But bash is still a lousy language.

This is where bash tracebacks come in…

“Whaaaaa? Bash has tracebacks?” I can hear you ask.

Yup, it can.

Check out the gist below. It is both a demonstration of the traceback as well as a template; grab the bits between the “cut here” and paste it into your own program.

# Tracebacks in bash
# Just take the code between the "cut here" lines
# and put it in your own program.
# Written by Christian Höltje
# Donated to the public domain in 2013

#--------->8---------cut here---------8<---------
set -eu

trap _exit_trap EXIT
trap _err_trap ERR

function _exit_trap
  local _ec="$?"
  if [[ $_ec != 0 && "${_showed_traceback}" != t ]]; then
    traceback 1

function _err_trap
  local _ec="$?"
  local _cmd="${BASH_COMMAND:-unknown}"
  traceback 1
  echo "The command ${_cmd} exited with exit code ${_ec}." 1>&2

function traceback
  # Hide the traceback() call.
  local -i start=$(( ${1:-0} + 1 ))
  local -i end=${#BASH_SOURCE[@]}
  local -i i=0
  local -i j=0

  echo "Traceback (last called is first):" 1>&2
  for ((i=start; i < end; i++)); do
    j=$(( i - 1 ))
    local function="${FUNCNAME[$i]}"
    local file="${BASH_SOURCE[$i]}"
    local line="${BASH_LINENO[$j]}"
    echo "     ${function}() in ${file}:${line}" 1>&2
#--------->8---------cut here---------8<---------

## Demos

function bomb
  trap _err_trap ERR
  local limit=${1:-5}
  echo -n " ${limit}"
  if [ "${limit}" -le 0 ]; then
    echo " BOOM"
    return 10
    bomb $(( limit - 1 ))

function stack
function stack_1
function stack_2
function stack_3

## Main

case "${1:-}" in
    echo -n "Counting down..."; bomb ;;
    echo "This shouldn't be shown because ${bad_variable} isn't set";;
    echo "Usage: $0 [bomb|badvar|true|false|stack]"


The gist (pun intended) of it that it traps ERR and EXIT interrupts in the shell. It then walks the FUNCNAME, BASH_SOURCE, and BASH_LINENO arrays to show where the callers were.

There is a little extra bits to ensure the traceback function itself doesn’t appear in the output and to format it nicely.

Not only are the tracebacks useful, but they make using set -eu much less painful. And you are using set -eu in your bash programs, right? Right?

I hope it is useful. If you have suggestions or questions, just ask!



Gravatar for stéphane gourichon
Stéphane Gourichon

Thank you for this interesting article. But the source code is not visible. Instead the text says : “Could not embed GitHub Gist 5889193: Must specify two-factor authentication OTP code.” Fortunately one can figure out the gist original url :

By the way: I found your blog due to your “it’s all text” firefox extension. Just like you I don’t like bash that much but use it when the cost-benefit analysis favors it, and it happens often for disposable hacks in a Linux environment. And I’ve been using set -eu in all my bash programs for years. Cheers!

Gravatar for docwhat

Ooh! Thanks for pointing that out. I just fixed it. The two-factor stuff is great, except for all these old places that got broken.

Submit a Comment


The personal blog of Christian Höltje.
docwhat docwhat contact