This content originally appeared on DEV Community and was authored by Umair-khurshid
When I was first starting out with some command line automation and development, I remember spending many hours trying to find the error in my code, and in the end, it could be something very simple, like a comma. You may have faced the same situation as well.
Knowing how to use proper debugging technique will help you resolve errors quickly. Unlike other languages, there is no debugging tool for bash where you can set breakpoints, step through code, etc. But you can enable some options in your scripts for debugging, which can help you, as follows:
Enable debugging flags from the shell when calling the script:
$ bash [debug flags] scriptname
Enable debugging options by passing debugging flags to the shebang line in the script.
#!/bin/bash [debug flags]
Enable debugging options using the script's set command.
set -o nounset
set -u
The command set
is a shell builtin command that can be used to control bash parameters and alter its behavior in certain ways. It will be widely used within shell scripts, either for debugging or to enable strict bash mode. Normally, you won't run configuration commands from the terminal to modify your shell's behavior.
Debugging
Before we get into the debugging options, I must tell you that you can debug either the entire script or just a certain part of the code. You must use the set command to enable and disable the debugging options.
set -[flags]
will enable debug mode.
set +[flags]
will disable debug mode.
Take a look at the following code. set -x
will enable xtrace mode for the script and set +x
will disable xtrace mode. Everything between set -x
and set +x
will run in xtrace debugging mode.
#!/bin/bash
set -x
read -p "Pass Dir name : " D_OBJECT
read -p "Pass File name : " F_OBJECT
set +x
touch ${D_OBJECT}/${F_OBJECT}
When working with variables in bash, the disadvantage is that if we try to use an undefined variable, the script will not fail with some error message like “Undefined variable”. Instead, it will print an empty string.
In the code below, I am getting user input and storing it in the $OBJECT
variable. I tried running the test operator (-f and -d) on the $OBJECT1
variable which is not defined.
#!/bin/bash
read -p "Object Name : " OBJECT
if [[ -f $OBJECT1 ]]
then
echo "$OBJECT is a file"
elif [[ -d $OBJECT1 ]]
then
echo "$OBJECT is a directory"
fi
When I run this code, it should have thrown me an error, but it didn't and even the script exited with the return code zero. To override this behavior, use the flag -or
,bash -u scriptname
. generating an error message when an undefined variable is used, displaying an Unbound variable error message .
You can also set -u using the set command or pass it as an argument to the shebang.
set -u
set -o nounset
#! /bin/bash -u
Xtrace – To the Rescue!
This is the mode I use when debugging bash scripts for logic errors. Xtrace mode will display the code line by line but with the parameters expanded. Now I can run the same script above, in xtrace mode and see exactly where the problem is occurring.
#!/bin/bash
read -p "Object Name : " OBJECT
if [[ -f $OBJECT1 ]]
then
echo "$OBJECT is a file"
elif [[ -d $OBJECT1 ]]
then
echo "$OBJECT is a directory"
fi
To debug this issue, I can run the script in xtrace mode passing the -x flag.bash -x scriptname. This tells me that there are empty strings assigned to the -f and -d conditional statements.
$ bash -x scriptname
+ read -p 'Object Name : ' OBJECT
Object Name: /test/
+ [[-f '']]
+ [[-d '']]
The + sign you see on stdout can be changed by putting the PS4 variable in the script. By default PS4 is set to (+), by throwing an echo $PS4 you will be able to verify it. To change it throw something like this:
$PS4=" >> " bash -x scriptname
changing the + sign to >> in the stdout of your script.
You can also set Xtrace mode using the set command or pass it as an argument to the shebang.
set -x set -o xtrace
#! /bin/bash -x
Don’t forget, that you can redirect debug logs to a file instead of printing them to the terminal. For example, to the code above add a few lines, after the shebang. assign a file descriptor 6 to the .log file and BASH_XTRACEFD=»6″ will redirect xtrace debug logs to file descriptor 6.
#!/bin/bash
exec 6> debug_output.log
BASH_XTRACEFD="6"
....
When I run this code instead of printing the output of xtrace to the terminal, it will be redirected to the output_debug.log file . Using cat , we will be able to read it inside the terminal.
Bash strict mode
To eliminate all the possible errors we have seen, you can add some options in each script.
- -e Exit the script if any command returns a non-zero exit code.
- -u Cause the script to fail if an undefined variable name is used.
- pipefail If any command in the pipeline fails, the exit code will be considered for the entire
- IFS Internal field separator, setting it to newline (\n) and (\t) will cause splitting to occur only on newline and tab.
set -e
set -u
set -o pipefail
EITHER
set -euo pipefail
IFS=$'\n\t'
Using TRAP
Imagine a scenario where your script is triggered, but you want to cancel it with a CTRL+C. In that case, SIGINT will be sent to your script. Using TRAP, you can catch this signal and execute some commands or functions.
The code below has a function called cleanup that will be executed when SIGINT is passed to the script.
trap 'cleaning' TERM INT
function clean(){
echo "Running cleanup, user used CTRL + C"
<some logic>
}
Print the code using Verbose mode
In verbose mode, the code will be printed before the result is returned. If the program requires interactive input, then only that line followed by a block of code will be printed.
Following this paragraph is a simple program that gets an object from the user and checks if object is a file or directory using a conditional statement.
#!/bin/bash
read -p "Object Name : " OBJECT
if [[ -f $OBJECT ]]
then
echo "$OBJECT is a file"
elif [[ -d $OBJECT ]]
then
echo "$OBJECT is a directory"
fi
Once the above code is executed, it will first print the code and then wait for user input, after which the rest of the code will be printed followed by the output. $OBJECT is either a directory or a file, as the case may be. As in the previous cases, you can set verbose mode using set or in the shebang.set -v,set -o verbose
either#! /bin/bash -v
Syntax errors – noexec mode
Syntax errors are very common in programs. You may have missed a quote or failed to exit the loop, etc. You can use the flag-n(noexec mode)
to validate the syntax before executing the program.
Yes, if you forgot to put a brace, a bracket, a quote or a comma, thanks to this flag, you will be able to know it by displaying syntax error messages. By default, when you run a script, bash will validate the syntax and throw these errors even without using the noexec mode. Likewise, as before, you can use set -n,set -o noexec
either#! /bin/bash -n
Conclusion
Well, good reader, I hope all these insights about debugging bash scripts are useful to you. The big difference with other programming languages is that it doesn't have debugging tools apart from some integrated options. Sometimes, these options will be more than enough to get the job done and can save you from a tight spot. Until another post!
This content originally appeared on DEV Community and was authored by Umair-khurshid
Umair-khurshid | Sciencx (2024-07-23T19:38:39+00:00) How to Debug Scripts in Bash. Retrieved from https://www.scien.cx/2024/07/23/how-to-debug-scripts-in-bash/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.