We write different shell scripts to perform different tasks. Have you ever encountered any errors while executing a shell script? The answer would be mostly yes! This is to be expected as it is practically impossible to always write perfect shell scripts, without errors or bugs.
For example, the following shell script is a buggy script while execution:
#!/bin/bash # Filename: buggy_script.sh # Description: Demonstrating a buggy script a=12 b=8 if [ a -gt $b ] then echo "a is greater than b" else echo "b is greater than a" fi
The following output is obtained after executing buggy_script.sh
:
$ sh buggy_script.sh buggy_script.sh: line 6: [: a: integer expression expected b is greater than a
From the output, we see that the error [: a: integer expression expected
occurred at line 6. It's not always possible to know the reason of the error by just looking into an error message, especially when seeing an error for the first time. Also, looking manually into the code and rectifying an error is difficult when dealing with a lengthy shell script.
To overcome all kinds of troubles while resolving an error or bug in a shell script, it's preferred to debug code. Debugging ways to debug a shell script are as follows:
echo
in an expected buggy area of a script to print the contents of the variables or commands to be executed-x
while running a script-x
and +x
options inside the scriptThe
echo
command is very useful as it prints whatever arguments are provided to it. When we encounter an error while executing a script, we know the line number with an error message. In such a case, we can use echo
to print what is going to be executed before the actual execution.
In our previous example, buggy_script.sh
, we got an error at line 6—that is if [ a -gt $b ]
—while execution. We can use the echo
statement to print what is actually going to be executed at line 6. The following shell script adds echo
in line 6, to see what will be executed finally at line 6:
#!/bin/bash # Filename: debugging_using_echo.sh # Description: Debugging using echo a=12 b=8 echo "if [ a -gt $b ]" exit if [ a -gt $b ] then echo "a is greater than b" else echo "b is greater than a" fi
We will now execute the debugging_using_echo.sh
script as follows:
$ sh debugging_using_echo.sh if [ a -gt 8 ]
We can see that the character a
is getting compared with 8
, while we were expecting the value of the variable a
. This means that, by mistake, we forgot to use $
with a
to extract the value of the variable a
.
Using
echo
to debug is easy if the script is small, or if we know where exactly the problem is. Another disadvantage of using echo
is that every time we make changes, we will have to open a shell script and modify the echo
command accordingly. After debugging, we will have to remember to delete the extra echo
lines added for the purposes of debugging.
To overcome these problems, bash provides the -x
option that can be used while executing a shell script. Running a script with the -x
option runs a script in the debug mode. This prints all the commands that are going to be executed along with the output of the script.
Consider the following shell script as an example:
#!/bin/bash # Filename : debug_entire_script.sh # Description: Debugging entire shell script using -x # Creating diretcories in /tmp dir1=/tmp/$1 dir2=/tmp/$2 mkdir $dir1 $dir2 ls -ld $dir1 ls -ld $dir2 rmdir $dir1 rmdir $dir2
Now, we will run the preceding script as follows:
$ sh debug_entire_script.sh pkg1 mkdir: cannot create directory '/tmp/': File exists drwxrwxr-x. 2 skumari skumari 40 Jul 14 01:47 /tmp/pkg1 drwxrwxrwt. 23 root root 640 Jul 14 01:47 /tmp/ rmdir: failed to remove '/tmp/': Permission denied
It gives an error that the /tmp/
directory already exists. By looking into the error, we can't say why it is trying to create the /tmp
directory. To trace the entire code, we can run the debug_entire_script.sh
script with the -x
option:
$ sh -x debug_entire_script.sh pkg1 + dir1=/tmp/pkg1 + dir2=/tmp/ + mkdir /tmp/pkg1 /tmp/ mkdir: cannot create directory '/tmp/': File exists + ls -ld /tmp/pkg1 drwxrwxr-x. 2 skumari skumari 40 Jul 14 01:47 /tmp/pkg1 + ls -ld /tmp/ drwxrwxrwt. 23 root root 640 Jul 14 01:47 /tmp/ + rmdir /tmp/pkg1 + rmdir /tmp/ rmdir: failed to remove '/tmp/': Permission denied
We can see that dir2
is /tmp/
. This means that no input is given to create the second directory.
Using the -v
option along with -x
makes debugging even more verbose because -v
displays input lines as it is:
$ sh -xv debug_entire_script.sh pkg1 #!/bin/bash # Filename : debug_entire_script.sh # Description: Debugging entire shell script using -x # Creating diretcories in /tmp dir1=/tmp/$1 + dir1=/tmp/pkg1 dir2=/tmp/$2 + dir2=/tmp/ mkdir $dir1 $dir2 + mkdir /tmp/pkg1 /tmp/ mkdir: cannot create directory '/tmp/': File exists ls -ld $dir1 + ls -ld /tmp/pkg1 drwxrwxr-x. 2 skumari skumari 40 Jul 14 01:47 /tmp/pkg1 ls -ld $dir2 + ls -ld /tmp/ drwxrwxrwt. 23 root root 640 Jul 14 01:47 /tmp/ rmdir $dir1 + rmdir /tmp/pkg1 rmdir $dir2 + rmdir /tmp/ rmdir: failed to remove '/tmp/': Permission denied
With verbose output, it is quite clear that the dir1 and dir2 variables are expecting a command line argument. So, two arguments must be provided from a command line:
$ sh debug_entire_script.sh pkg1 pkg2 drwxrwxr-x. 2 skumari skumari 40 Jul 14 01:50 /tmp/pkg1 drwxrwxr-x. 2 skumari skumari 40 Jul 14 01:50 /tmp/pkg2
To debug a shell script, it's not necessary to debug the entire script all the time. Sometimes, debugging a partial script is more useful and time-saving. We can achieve partial debugging in a shell script using the set
builtin command:
set -x (Start debugging from here) set +x (End debugging here)
We can use set +x
and set -x
inside a shell script at multiple places depending upon the need. When a script is executed, commands in between them are printed along with the output.
Consider the following shell script as an example:
#!/bin/bash # Filename: eval.sh # Description: Evaluating arithmetic expression a=23 b=6 expr $a + $b expr $a - $b expr $a * $b
Executing this script gives the following output:
$ sh eval.sh 29 17 expr: syntax error
We get the syntax error with an expression that is most likely the third expression—that is, expr $a * $b
.
To debug, we will use set -x
before and set +x
after expr $a * $b
.
Another script partial_debugging.sh
with partial debugging is as follows:
#!/bin/bash # Filename: partial_debugging.sh # Description: Debugging part of script of eval.sh a=23 b=6 expr $a + $b expr $a - $b set -x expr $a * $b set +x
The following output is obtained after executing the partial_debugging.sh
script:
$ sh partial_debugging.sh 29 17 + expr 23 eval.sh partial_debugging.sh 6 expr: syntax error + set +x
From the preceding output, we can see that expr $a * $b
is executed as expr 23 eval.sh partial_debugging.sh 6
. This means, instead of doing multiplication, bash is expanding the behavior of *
as anything available in the current directory. So, we need to escape the behavior of the character *
from getting expanded—that is, expr $a * $b
.
The following script eval_modified.sh
is a modified form of the eval.sh
script:
#!/bin/bash # Filename: eval_modified.sh # Description: Evaluating arithmetic expression a=23 b=6 expr $a + $b expr $a - $b expr $a * $b
Now, the output of running eval_modified.sh
will be as follows:
$ sh eval_modified.sh 29 17 138
The script runs perfectly now without any errors.
Other than what we have learned in debugging, you can also use the bashdb
debugger for even better debugging of the shell script. The source code and documentation for bashdb
can be found at http://bashdb.sourceforge.net/.
3.138.116.228