In the very old days of computer technology, the initial problems with computers were due to real insects. Due to this, fault finding was later called findingthebug. Therefore, the process of finding and fixing the problems in computers was called debugging.
The process of debugging involves the following:
- Finding out what has gone wrong
- Fixing the problem
In the actual debugging process, you need to do the following:
- Understand the error message and find out what the problem is with the script.
- Find the error location in the script.
- Locate the line number from the error message. The following are a few error messages:
debug_sp: line 11: [7: command not found]file: line 6: unexpected EOF while looking for matching `"'
These messages inform the user about the line numbers of the script that contain errors.
- Correct the issue or problematic part of code. We may have to read the line as well as look backward from this line number for any possible reason for the error.
Debugging mode – disabling the shell (option -n)
In the Bash shell, the-noption is ashortcutfornoexec(as in no execution). This option tells the shell not to run the commands. Instead, the shell just checks for syntax errors.
We can test the script as follows:
$ bash -n hello.sh
The-noption will tell the Bash shell to check the syntax in the Shell script but not to execute the Shell script.
Another way to do this is as follows:
#!/bin/bash -n
We have modified shebang line.
In this case, we can test the Shell script as follows:
$ chmod u+x hello.sh
$ ./hello.sh
This option is safe, since the shell commands are not executed. We can catch incomplete if, for, while, case, and similar programming constructs as well as many more syntactical errors.
Let’s writedebug_01.sh:
#!/bin/bash
echo -n "Commands in bin directory are : $var"
for var in $(ls )
do
echo -n -e "$var "
do
# no error if "done" is typed instead of "do"
Save the file, give the permission to execute, and run the script as follows:
$ chmod u+x debug_01.sh
$ ./debug_01.sh
This should produce the following output:
Commands in bin directory are : ./hello.sh: line 7: syntax error near unexpected token `do'./hello.sh: line 7: `do'
$ bash -n debug_01.sh
This should produce the following output:
Output:hello.sh: line 7: syntax error near unexpected token `do'hello.sh: line 7: `do'
Debugging mode – displaying commands (option -v)
The-voption tells theshellto run inverbosemode. In practice, this means that the shell will echo each command prior to executing the command. This will beusefulin locating the line of script that has created an error.
We can enable the script execution with the-voption as follows:
$ bash -v hello.sh
Another way is by modifying the shebang line as follows:
#!/bin/bash -v
In this case, we can run the script with the -v option as follows:
$ chmod u+x hello.sh
$ ./hello.sh
Let’s write the debug_02.sh script as follows:
#!/bin/bash
echo "Hello $LOGNAME"
echo "Today is `date`
echo "Your present working directory is $PWD
echo Good-bye $LOGNAME
Save the file, give the permission to execute, and run the script as follows:
$ chmod u+x debug_02.sh
$ ./debug_02.sh
This should produce the following output:
Output:Hello studentToday is Fri May 1 00:18:52 IST 2015Your present working directory is /home/student/workGood-bye student
Let’s enable the -v option for debugging, and run the script again as follows:
$ bash -v debug_02.sh
This should produce the following output:
#!/bin/bash
echo "Hello $LOGNAME"
"Hello student"
echo "Today is `date`
date
"Today is Fri May 1 00:18:52 IST 2015
echo "Your present working directory is $PWD
"Your present working directory is /home/student/work
echo Good-bye $LOGNAME
Good-bye student
Debugging mode – the tracing execution (option -x)
The-xoption, short forxtraceor execution trace, tells the shell to echo each command after performing the substitution steps. Thus, we willseethe value of variables and commands.
We can trace the execution of the Shell script as follows:
$ bash -x hello.sh
Instead of the previous way, we can modify the shebang line as follows:
#!/bin/bash -x
Let’s test the earlierdebug_01.shscript as follows:
$ bash -x hello.sh
Output:$ bash -x debug_02.sh+ echo Hello studentHello student+ date+ echo The date is Fri May 1 00:18:52 IST 2015The date is Fri May 1 00:18:52 IST 2015+ echo Your home shell is /bin/bashYour home shell is /bin/bash+ echo Good-bye studentGood-bye student
Let’s try the following programs with the -n, -v, -f, and -x options. Here’s a sample program,-debug_03.sh:
#!/bin/bash
echo "Total number of parameters are = $#"
echo "Script name = $0"
echo "First Parameter is $1"
echo "Second Parameter is $2"
echo "All parameters are = $*"
echo "File names starting with f* in current folder are :"
ls f*
Save the file, give the permission to execute, and run the script as follows:
$ chmod u+x debug_03.sh
$ ./debug_03.sh One Two
This should produce the following output:
Output:"Total number of parameters are = 2""Script name = ./debug_03.sh""First Parameter is India""Second Parameter is Delhi""All parameters are = India Delhi""File names starting with debug_02.sh debug_03.sh in current folder are: "debug_02.sh debug_03.sh
Let’s test the same script with the -n option, which will check for syntax errors:
$ bash -n debug_03.sh One Two
Let’s test the same script with the -v option:
$ bash -v debug_03.sh One Two
This should produce the following output:
Output:#!/bin/bashecho "Total number of parameters are = $#""Total number of parameters are = 2"echo "Script name = $0""Script name = debug_03.sh"echo "First Parameter is $1""First Parameter is India"echo "Second Parameter is $2""Second Parameter is Delhi"echo "All parameters are = $*""All parameters are = India Delhi"echo "File names starting with d* in current folder are :""File names starting with debug_02.sh debug_03.sh in current folder are: "ls d*debug_02.sh debug_03.sh
Let us test the same script with the -x option:
$ bash -x debug_03.sh One Two
This should produce the following output:
Output:+ echo $'342200234Total' number of parameters are = $'2342200235'"Total number of parameters are = 2"+ echo $'342200234Script' name = $'debug_03.sh342200235'"Script name = debug_03.sh"+ echo $'342200234First' Parameter is $'India342200235'"First Parameter is India"+ echo $'342200234Second' Parameter is $'Delhi342200235'"Second Parameter is Delhi"+ echo $'342200234All' parameters are = India $'Delhi342200235'"All parameters are = India Delhi"+ echo $'342200234File' names starting with debug_02.sh debug_03.sh in
current folder are $':342200234'"File names starting with debug_02.sh debug_03.sh in current folder are: "+ ls debug_02.sh debug_03.shdebug_02.sh debug_03.sh
Let’s test one more program, which will give a syntax error during the -n and -x options debugging. Write the debug_04.sh Shell script as follows:
#!/bin/bash
echo "Commands in bin directory are : $var"
for var in $(ls )
do
echo -n -e "$var "
do
Save the file, give the permission to execute, and run the script as follows:
$ chmod u+x debug_04.sh
$ bash -n debug_04.sh
This should produce the following output:
Output:debug_04.sh: line 7: syntax error near unexpected token `do'debug_04.sh: line 7: `do'
The preceding program has a syntax error on line number 7. The word do has an error. We need to change word do to done
Summarizing the debugging options for the Bash shell
The following is a summary of various debugging options used for debugging, such as -x, -v, and -n with their details:
$ bash -n script_name // interpretation without execution
$ bash -v script_name // Display commands in script
$ bash -x script_name // Trace the execution of script
$ bash -xv script_name // Enable options x and v for debugging
$ bash +xv script_name //Disable options x and v for debugging
Using the set command
Most of the time, we invoke the debugging mode from the first line of script. This debugging mode will remain active until the last line of code. But many times, we may need to enable debugging for a particular section of script. By using the set command, we can enable and disable debugging at any point in our shell script:
set -xsection of scriptset +x
Consider the following script:
#!/bin/bash
str1="USA"
str2="Canada";
[ $str1 = $str2 ]
echo $?
Set -x
[ $str1 != $str2 ]
echo $?
[ -z $str1 ]
echo $?
Set +x
[ -n $str2 ]
echo $?
Exit 0
In this case, the debugging will be enabled after the set -x and will be disabled immediately after the set +x
Summary of debugging options for the set command
The following table summarizes the various options for the set command:
Short notation
Result
set -f
Disables globing. In this case, the filename expansions using wildcards or meta-characters will be disabled.
set -v
This will print the shell script lines as they are read by the shell.
set -x
This option will display each line after the variable substitution and command expansion, but before execution by the shell. This option is often called shell tracing.
set -n
This reads all commands and checks the syntax, but does not execute them.
The vi editor setting for debugging
For general debugging, we canusethe vi editor along with certain options.
During debugging, many times we search for a pattern throughout the complete document. It is preferable to highlight the searched item. We will enable search pattern highlighting by using the following command in the vi editor when the document is opened:
:set hlsearch:set ic
We can even modify the vi editor configuration file-.exrc or .vimrc so that we need not give the previous command again and again.
Good practices for Shell scripts
If we follow certain good practices, then we will face errors. Even if errors are found, these will be easier to debug:
- Clear and tidy the script.
- Try to properly indent the programming constructs, such as if, for, while, and other similar loops:
if [ $rate -lt 3 ]then echo "Sales tax rate is too small."fi
- Do not put multiple commands on the same line by using;.
- Use descriptive variable names, such assalary, instead ofsa. In very complex Shell scripts, non-descriptive variable names will make debugging very difficult.
- Store the file and directory names in variables instead of typing them again and again. If any change is required in the directory path, then making the change in the variable at one place will be sufficient:
WORKING_DIR=$HOME/workif [ -e $WORKING_DIR]then # Do something....fi
- Use comments for an easier understanding of the script. This will make debugging easier to others. If it contains tricky or complex commands, then, even after a few months, we will need comments to understand our own script. A cute little trick today may become a challenge tomorrow.
- Print informative error messages. Write simpler scripts. Use simpler if, case, for, and or functions. It has been practically observed that if scripts are simpler, then such scripts are easy to maintain over a long period of time, such as a few years.
- Test the script again and again with various test scenarios and test cases. Check for all possibilities of human error, such as bad input, insufficient arguments, non-existent files, and similar possibilities.
