Debugging your scripts

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:

  • Using echo in an expected buggy area of a script to print the contents of the variables or commands to be executed
  • Debugging an entire script using -x while running a script
  • Debugging a section of a script using set builtin command with the -x and +x options inside the script

Debugging using echo

The 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.

Debugging an entire script using -x

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

Now, the script works without any errors.

Note

Instead of passing the -xv options to bash from a command line, we can add it in the shebang line in the script file—that is, #!/bin/bash -xv.

Debugging sections of a script using the set options

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/.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.179.177