Last Updated: March 07, 2016
·
1.426K
· markm

Debugging BASH scripts

Here's a tip (or two) that you can use to debug a bash script like you would a more structured language.

Error handling

#!/bin/bash

LOGFILE=/tmp/err.log
function catcherr() {
    rval=$?
    line=$1
    echo "$(date): Died with return value [$rval] at line number [$line]" >>$LOGFILE >/dev/stderr
}

trap 'catcherr $LINENO' ERR

false

echo "done"

Simply: trap sets up a condition that calls catcherr when a non-zero return value occurs during your script.

Line by line: We define the log file first. I typically run most of my scripts headlessly through a scheduler, so a log file is more useful than STDOUT or STDERR.

LOGFILE=/tmp/err.log

To handle script errors you need to have some code to run when the error is thrown. The cleanest way to do this is with a simple function.

function catcherr() {
    rval=$?
    line=$1
    echo "$(date): Died with return value [$rval] at line number [$line]" >>$LOGFILE >/dev/stderr
}

The function first grabs the return value of the last command executed ($?). This must be the first line in the function as it's overwritten with each line executed. $1 is the first argument of the function.

The echo statement is the "meat" of the error handling. It appends the message to $LOGFILE, spits it to STDERR just in case and logs the return value & line number with the timestamp. You can customise your timestamp in the $(date) function too; check out date --help. You may want an 'exit' at the end of this function, without it bash will resume executing after the error.

The trap call is what wires the code to execute (the first argument) and the triggering event. The first arg is evaluated at the time of the event, so the argument to the catcherr function is bash's internal environment variable $LINENO, which will be the line being executed when the ERR trigger was hit.

trap 'catcherr $LINENO' ERR

The ERR trigger is fired when a non-zero return value occurs on a line in your script. This can be on a missing command or just unexpected errors. Some functions will return non-zero values intentionally, such as grep. You'll need to make sure you either run these in conditional blocks if you don't want to trigger this trap.

For more info on trap (including the different triggers, like DEBUG), check it's man entry out: http://www.gnu.org/software/bash/manual/bashref.html#index-trap

For more info on the bash internal environment variables (particularly FUNCNAME) check out the man entry: http://www.gnu.org/software/bash/manual/bashref.html#Bash-Variables

2 Responses
Add your response

I usually go with set -x. But this seems like a good addition to my toolbox, even works in zsh. Thanks :)

over 1 year ago ·

I didn't know about -x; thanks. It looks like it'll take care of the 80% simple cases but this is handy for something a bit more customised.

over 1 year ago ·