In this article I’m going to give you a hands-on introduction to standard bash debugging techniques.
In addition, you’ll learn some techniques to make your bash scripts more robust to failure.
This article uses the hard way method, which emphasises hands-on-keyboard work to embed the learning. You’re going to have to think and type to learn.
Syntax Checking Options
Start by creating this simple script:
$ mkdir -p lbthw_debugging $ cd lbthw_debugging $ cat > debug_script.sh << 'END' #!/bin/bash A=some value echo "${A} echo "${B}" END $ chmod +x debug_script.sh
Now run it with the -n flag like this:
$ bash -n debug_script.sh -n
This flag only parses the script, rather than actually running it. It’s useful for detecting basic syntax errors.
You’ll see it’s broken. Fix it. Then run it again.
If you’re not sure how to fix, contact me.
Verbose and Trace Flags
Now run with -v
to see the verbose output.
$ bash -v debug_script.sh
and then run with -x
to trace the output:
$ bash -x debug_script.sh
What do you notice about the output of the commands? Read them carefully.
Do you see the problem?
Using these flags together can help debug scripts where there is an elementary error, or even just working out what’s going on when a script runs. I used -x
only yesterday to figure out why a systemctl
service wasn’t running or logging.
Material here based on the ‘advanced’ section of my book
Learn Bash the Hard Way.
Free preview available here.
Managing Variables
Variables are a core part of most serious bash scripts (and even one-liners!), so managing them is another important way to reduce the possibility of your script breaking.
Change your script to add the ‘set’ line immediately after the first line and see what happens:
#!/bin/bash set -o nounset A="some value" echo "${A}" echo "${B}"
Now research what the nounset
option does. Which set
flag does this correspond to?
Now, without running it, try and figure out what this script will do. Will it run?
#!/bin/bash set -o nounset A="some value" B= echo "${A}" echo "${B}"
I always set nounset
on my scripts as a habit. It can catch many problems before they become serious.
Tracing Variables
If you are working with a particularly complex script, then you can get to the point where you are unsure what happened to a variable.
Try running this script and see what happens:
#!/bin/bash
set -o nounset
declare A="some value"
function a {
echo "${BASH_SOURCE}>A A=${A} LINENO:${1}"
}
trap "a $LINENO" DEBUG
B=value
echo "${A}"
A="another value"
echo "${A}"
echo "${B}"
There’s a problem with this code. The output is slightly wrong. Can you work out what is going on? If so, try and fix it.
You may need to refer to the bash man page, and make sure you understand quoting in bash properly.
It’s quite a tricky one to fix ‘properly’, so if you can’t fix it, or work out what’s wrong with it, then ask me directly and I will help.
Profiling Bash Scripts
Returning to the xtrace
(or set -x
flag), we can exploit its use of a PS
variable to implement the profiling of a script:
#!/bin/bash set -o nounset set -o xtrace declare A="some value" PS4='$(date "+%s%N => ")' B= echo "${A}" A="another value" echo "${A}" echo "${B}" ls pwd curl -q bbc.co.uk
From this you should be able to tell what PS4
does. Have a play with it, and read up and experiment with the other PS
variables to get familiar with what they do.
NOTE: If you are on a Mac, then you might only get
second-level granularity on the date!
Linting with Shellcheck
Finally, here is a very useful tip for understanding bash more deeply and improving any bash scripts you come across.
Shellcheck is a website and a package available on most platforms that gives you advice to help fix and improve your shell scripts. Very often, its advice has prompted me to research more deeply and understand bash better.
Here is some example output from a script I found on my laptop:
$ shellcheck shrinkpdf.sh In shrinkpdf.sh line 44: -dColorImageResolution=$3 \ ^-- SC2086: Double quote to prevent globbing and word splitting. In shrinkpdf.sh line 46: -dGrayImageResolution=$3 \ ^-- SC2086: Double quote to prevent globbing and word splitting. In shrinkpdf.sh line 48: -dMonoImageResolution=$3 \ ^-- SC2086: Double quote to prevent globbing and word splitting. In shrinkpdf.sh line 57: if [ ! -f "$1" -o ! -f "$2" ]; then ^-- SC2166: Prefer [ p ] || [ q ] as [ p -o q ] is not well defined. In shrinkpdf.sh line 60: ISIZE="$(echo $(wc -c "$1") | cut -f1 -d\ )" ^-- SC2046: Quote this to prevent word splitting. ^-- SC2005: Useless echo? Instead of 'echo $(cmd)', just use 'cmd'. In shrinkpdf.sh line 61: OSIZE="$(echo $(wc -c "$2") | cut -f1 -d\ )" ^-- SC2046: Quote this to prevent word splitting. ^-- SC2005: Useless echo? Instead of 'echo $(cmd)', just use 'cmd'.
The most common reminders are regarding potential quoting issues, but you can see other useful tips in the above output, such as preferred arguments to the test
construct, and advice on “useless” echo
s.
Exercise
1) Find a large bash script on a social coding site such as GitHub, and run shellcheck
over it. Contribute back any improvements you find.
If you like this, you might like one of my books:
Seriously, where has shellcheck been all my life?!
And thank you for also explaining `bash -n`, `bash -v`, & `bash -x`.
Wow, seconded on shellcheck! This is a life saver!