- Enabling Debugging
- Using Syntax Checking
- Shell Tracing
- Summary
- Questions
- Terms
Using Syntax Checking
When dealing with any shell script, it is a good idea to check the syntax of the script before trying to execute it. This will help you find and fix most problems.
To enable syntax checking, use the -n option as follows:
/bin/sh -n script arg1 arg2 ... argN
Here script is the name of a script and arg1 through argN are the arguments for that script. If there are syntax errors in script, the shell generates an error message that indicates the source of the error.
Check the syntax of the following script (the line numbers are included for your reference) and see if you can spot the error:
1 #!/bin/sh 2 3 YN=y 4 if [ $YN = "yes" ] 5 echo "yes" 6 fi
If this script is stored in the file buggy1.sh, you can check its syntax as follows:
$ /bin/sh -n ./buggy1.sh
The output looks like the following:
./buggy1.sh: syntax error at line 7: 'fi' unexpected
This tells you that when the shell tried to read line 7, it found that the fi statement on line 6 was unexpected. By now you have probably figured out that the reason the shell was trying to read line 7 is that the if statement on line 4 is not properly terminated with a then statement:
if [ $YN = "y" ]
This line should read as:
if [ $YN = "y" ] ; then
By making this change, you will find that the command
$ /bin/sh -n buggy1.sh
produces no output, indicating that there are no syntax errors in the script.
Why Syntax Checking Is Important
After looking at the shell script in the previous example, you might be wondering why you couldn't just execute the shell script to determine the problem. After all, the output of the command:
$ /bin/sh ./buggy1.sh buggy1.sh: syntax error at line 7: 'fi' unexpected
is identical to the output of the command:
$ /bin/sh -n ./buggy1.sh
In this particular instance, it does not make a difference, but this is not always the case. As an example, consider the following script (the line numbers are included for your reference):
1 #!/bin/sh 2 3 Failed() { 4 if [ $1 -ne 0 ] ; then 5 echo "Failed. Exiting." ; exit 1 ; 6 fi 7 echo "Done." 8 } 9 10 echo "Deleting old backups, please wait... \c" 11 rm -r backup > /dev/null 2>&1 12 Failed $? 13 14 echo "Make backup (y/n)? \c" 15 read RESPONSE 16 case $RESPONSE in 17 [yY]|[Yy][Ee][Ss]|*) 18 echo "Making backup, please wait... \c" 19 cp -r docs backup 20 Failed 21 [nN]|[Nn][Oo]) 22 echo "Backup Skipped." ;; 23 esac
There are at least three errors in this script. See if you can find them.
CAUTION
You should not try to run this script until you have found and fixed the bugs it contains.
If this script is in a file called buggy2.sh, executing it produces the following output:
Deleting old backups, please wait... Done. Make backup (y/n)?
Entering y at the prompt produces the following error:
./buggy3.sh: syntax error at line 21: ')' unexpected
Due to a bug in the script, you can't make a backup, and you have already lost your previous backup. As you can imagine, this is a very bad situation.
The reason the script doesn't detect the error earlier is due to the manner in which the shell reads and executes scripts; it reads and executes each line of a shell script individually, just like it does on the command line. In this case the shell reads and executes lines until it encounters a problem.
When the -n option is specified, the shell does not execute the script. It just checks the syntax of each line. In the previous example using this option would have avoided the situation encountered by running the script.
Using Verbose Mode
Now that you know why syntax checking should be employed, let's track down the source of the problem by looking at line 21 of buggy2.sh:
21 [nN]|[Nn][Oo])
does not provide sufficient context to determine the source of the problem. Sometimes knowing where a syntax error occurs is not enoughyou have to know the context in which the error occurs. In order to determine the context of the problem, you can use the -v (v as in verbose) debugging mode. When this option is specified, the shell prints each line of a script as it is read.
If the -v option is specified by itself, the shell executes every line in the script. Because you want to just check the syntax, you need to combine the -n and -v options as follows:
$ /bin/sh -nv script arg1 arg2 ... argN
If you execute buggy2.sh with these debugging options
$ /bin/sh -nv ./buggy2.sh
the output looks like the following (the line numbers are provided for your reference):
1 #!/bin/sh 2 3 Failed() { 4 if [ $1 -ne 0 ] ; then 5 echo "Failed. Exiting." ; exit 1 ; 6 fi 7 echo "Done." 8 } 9 10 echo "Deleting old backups, please wait... \c" 11 rm -r backup > /dev/null 2>&1 12 Failed $? 13 14 echo "Make backup (y/n)? \c" 15 read RESPONSE 16 case $RESPONSE in 17 [yY]|[Yy][Ee][Ss]) 18 echo "Making backup, please wait... \c" 19 cp -r docs backup 20 Failed 21 [nN]|[Nn][Oo]) ./buggy2.sh: syntax error at line 21: ')' unexpected
Based on this output, the problem is apparent: Line 20 does not terminate the first pattern of the case statement with ;;. You can make either of the following changes to fix the script:
Failed ;;
or
Failed ;;
After making either of these change, you find that the command
$ /bin/sh -n buggy2.sh
does not produce an error message. As you will see in the next section, this does not necessarily mean that the script is bug free.