- You need a router-based VPN in 2025. How why and how to set one up
- How To Fight Scattered Spider Impersonating Calls to The IT Help Desk
- How to upgrade your 'incompatible' Windows 10 PC to Windows 11 in 2025
- Can you still get a Windows 10 upgrade for free in 2025? Short answer: Maybe
- How Government Agencies Can Level the Cybersecurity Playing Field With AI/ML
Troubleshooting your bash scripts
If you run into problems building, testing or running complex bash scripts, don’t lose heart. There are many ways you can help ensure that your scripts will work flawlessly. In this post, we’ll examine some ways you can lessen the likelihood of errors and how to go about doing some simple but very effective troubleshooting.
Through a combination of robust logic that tests for possible problems and some troubleshooting to help detect errors, your scripts are likely to be ready for showtime very quickly.
Building the outer edges first
One way to avoid syntactical errors in scripts is to start your for and while loops, case statements and if/then commands using the outer logic first. If you start your script logic using a syntactical “skeleton”, you won’t forget to end it properly.
for i in list while [condition] case "$1" in if [ condition ]; then do do str1) cmd;; cmds cmds cmds *) cmd;; else
done done esac cmds
fi
Go back and fill in the details after you put the basic command framework in place.
Verifying script arguments
To help ensure that a script won’t run into runtime problems, make sure it checks that the correct number of arguments are being provided and that any files provided as arguments actually exist. Here are some examples of implementing these checks.
Check the # of arguments
if [ $# -lt 2 ]; then echo "Usage: $0 lines filename" exit else numlines=$1 filename=$2 fi
Notice that a usage statement is generated if the arguments don’t match what is expected, and the script is then exited.
Check the type of an argument
if [[ $numlines != [0-9]* ]]; then echo "Error: $numlines is not numeric" exit fi
Check that a file exists
if [ ! -f $filename]; then echo "Error: File $filename not found" exit fi
Turning on bash debugging
Another way to test scripts it to turn on debugging. This will help you verify that they’re working properly or pinpoint any lines in the script that might be causing problems. You can turn on what is often called “debug mode” by using -x as an argument to bash. It helps in debugging by displaying each line in a script as it is being executed. This allows you to see which commands are working as expected and which are not.
You can use -x in one of two ways. You can start your script like this:
$ bash -x myscript
or you can insert the -x into the “shebang line” at the top of the script:
#!/bin/bash -x
Putting the -x option in the shebang line means you don’t have to type “bash -x scriptname”. Just remember to remove the “-x” once your debugging is complete.
You can also elect to turn debugging on for only a portion of a script. To do this, add “set -x” before the section of the script you want debug and “set +x” to turn debugging off after that section.
set -x while [ $i -le 2 ] do echo Number: $i ((i++)) done set +x
The -v bash option works similarly to -x, but it displays each line in a script as it being read. This means that you will see the entire script and then its output as it is run. You can also use both with -xv as you bash argument (i.e., bash -xv).
Here’s an example of using the bash -x form of debugging for a script that is intended to display the top lines of a file:
$ bash -x showtop ++ date +%s + STARTTIME=1622138266 ++ - 1622138266 showtop: line 4: -: command not found <=== oops! + echo 'It takes seconds to complete this task...' It takes seconds to complete this task... + '[' 0 -lt 2 ']' + echo 'Usage: showtop lines filename' Usage: showtop lines filename + exit
The fourth line of theh below output indicates that there’s a problem. For some reason, the script is looking for a command named “-“. Soon after, we see a usage statement indicating something is wrong with the arguments and the line above suggests that none were provided. In this case, the first error came about because $ENDTIME has no value at that point in the script, making $ENDTIME equal to an empty string, thus the calculation on the second line ends up being just “- 1622138266” and is clearly invalid.
#!/bin/bash STARTTIME=$(date +%s) timing=`expr $ENDTIME - $STARTTIME` echo "It takes $timing seconds to complete this task"
That timing calculation belongs near the bottom of the script following collecting the ending time value using the date command as is in this corrected version:
#!/bin/bash STARTTIME=$(date +%s) if [ $# -lt 2 ]; then echo "Usage: $0 lines filename" exit else numlines=$1 filename=$2 fi if [[ $numlines != [0-9]* ]]; then echo "Error: $numlines is not numeric" exit fi if [ ! -f $filename ]; then echo "Error: File $filename not found" exit else echo top of file head -$numlines $filename fi ENDTIME=$(date +%s) timing=`expr $ENDTIME - $STARTTIME` echo "It took $timing seconds to complete this task"
Below is an example of running the repaired script in debug mode. Some of bottom lines (those shown in bold) are the intended script output. The other lines are the debugging output.
$ bash -x showtop 10 oldScript ++ date +%s + STARTTIME=1622143586 + '[' 2 -lt 2 ']' + numlines=3 + filename=oldScript + [[ 3 != [0-9]* ]] + '[' '!' -f oldScript ']' + echo top of file top of file + head -3=10 oldScript #!/bin/bash STARTTIME=$(date +%s) echo "It takes $($ENDTIME - $STARTTIME) seconds to complete this task..." if [ $# -lt 2 ]; then echo "Usage: $0 lines filename" exit else numlines=$1 ++ date +%s + ENDTIME=1622143586 ++ expr 1622143586 - 1622143586 + timing=0 + echo 'It took 0 seconds to complete this task...' It took 0 seconds to complete this task...
Wrap-Up
Having scripts verify arguments and using the -x debugging option to pinpoint flaws can be extremely handy in getting scripts ready for use, especially for those which are far longer and more complicated than the example used in this post.
Copyright © 2021 IDG Communications, Inc.