Flylib.com

Books Software

 
 
 

14.9. Debugging

 <  Day Day Up  >  

14.9. Debugging

By using the “n option to the bash command, you can check the syntax of your scripts without really executing any of the commands. If there is a syntax error in the script, the shell will report the error. If there are no errors, nothing is displayed.

The most commonly used method for debugging scripts is the set command with the “x option, or bash invoked with the “x option and the script name . See Table 14.7 for a list of debugging options. These options allow an execution trace of your script. Each command from your script is displayed after substitution has been performed, and then the command is executed. When a line from your script is displayed, it is preceded with a plus ( + ) sign.

Table 14.7. Debugging Options

Command

Option

What It Does

bash “x scriptname

Echo option

Displays each line of script after variable substitutions and before execution.

bash “v scriptname

Verbose option

Displays each line of script before execution, just as you typed it.

bash “n scriptname

Noexec option

Interprets but does not execute commands.

set “x

Turns on echo

Traces execution in a script.

set +x

Turns off echo

Turns off tracing.


With the verbose option turned on, or by invoking the shell with the “v option ( bash “v scriptname ), each line of the script will be displayed just as it was typed in the script, and then executed. (See Chapter 15, "Debugging Shell Scripts," on page 967 for details.)

Example 14.69.
(The Script)

    #!/bin/bash

# Scriptname: todebug

1   name="Joe Shmoe"

    if [[ $name == "Joe Blow" ]]

    then

        printf "Hello $name\n"

    fi



    declare -i num=1

    while (( num < 5 ))

    do

       let num+=1

    done

    printf "The total is %d\n", $num



(The Output)

2

bash -x todebug


+ name=Joe Shmoe


+ [[ Joe Shmoe == \J\o\e\ \B\l\o\w ]]


+ declare -i num=1


+ ((  num < 5  ))


+ let num+=1


+ ((  num < 5  ))


+ let num+=1


+ ((  num < 5  ))


+ let num+=1


+ ((  num < 5  ))


+ let num+=1


+ ((  num < 5  ))


+ printf 'The total is %d\n,' 5


The total is 5


EXPLANATION

  1. The script is called todebug . You can watch the script run with the “x switch turned on. Each iteration of the loop is displayed and the values of variables are printed as they are set and when they change.

  2. Bash is invoked with the “x option. Echoing is turned on. Each line of the script will be displayed to the screen prepended with a plus sign ( + ). Variable substitution is performed before the line is displayed. The result of the execution of the command appears after the line has been displayed.

 <  Day Day Up  >  
 <  Day Day Up  >  

14.10. The Command Line

14.10.1 Processing Command-Line Options with getopts

If you are writing scripts that require a number of command-line options, positional parameters are not always the most efficient. For example, the UNIX ls command takes a number of command-line options and arguments. (An option requires a leading dash; an argument does not.) Options can be passed to the program in several ways: ls “laFi , ls “i “a “l “F , ls “ia “F , and so forth. If you have a script that requires arguments, positional parameters might be used to process the arguments individually, such as ls “l “i “F . Each dash option would be stored in $1 , $2 , and $3 , respectively. But, what if the user listed all of the options as one dash option, as in ls “liF ? Now the “liF would all be assigned to $1 in the script. The getopts function makes it possible to process command-line options and arguments in the same way they are processed by the ls program. [8] The getopts function will allow the runit program to process its arguments using any variety of combinations.

[8] See the UNIX/Linux manual pages (Section 3) for the C library function getopt .

Example 14.70.
(The Command Line)

1   $

runit x n  200 filex

2   $

runit xn200 filex

3   $

runit xy

4   $

runit yx n 30

5   $

runit n250 xy filey

(any other combination of these arguments)

EXPLANATION

  1. The program runit takes four arguments: x is an option, n is an option requiring a number argument after it, and filex is an argument that stands alone.

  2. The program runit combines the options x and n and the number argument 200 ; filex is also an argument.

  3. The program runit is invoked with the x and y options combined.

  4. The program runit is invoked with the y and x options combined; the n option is passed separately, as is the number argument, 30 .

  5. The program runit is invoked with the n option combined with the number argument, the x and y options are combined, and the filey is separate.

Before getting into all the details of the runit program, we examine the line from the program where getopts is used to see how it processes the arguments.

Example 14.71.
(A line from the script called runit)

while getopts :xyn: name


EXPLANATION

x , y , and n are the options. In this example the first option is preceded by a colon . This tells getopts to use silent error reporting. If there is a colon after one of the options, the option expects an argument separated from it by whitespace. An argument is a word that does not begin with a dash. “n requires an argument.

Any options typed at the command line must begin with a dash.

Any options that do not contain a dash tell getopts that the option list has ended.

Each time getopts is called, it places the next option value it finds in the variable name . (You can use any variable name here.) If an illegal argument is given, name is assigned a question mark.

Sample getopts Scripts

The following examples illustrate how getopts processes arguments.

Example 14.72.
(The Script)

    #!/bin/bash

# Program: opts1


# Using getopts  First try

1

while getopts xy options

do

2       case $options in

3       x) echo "you entered x as an option";;

        y) echo "you entered y as an option";;

        esac

    done



(The Command Line)

4   $

opts1 x


you entered x as an option

5   $

opts1 xy


you entered x as an option


you entered y as an option

6   $

opts1 y


you entered y as an option

7   $

opts1 b


opts1:   illegal option --  b

8    $

opts1 b


EXPLANATION

  1. The getopts command is used as a condition for the while command. The valid options for this program are listed after the getopts command; they are x and y . Each option is tested in the body of the loop, one after the other. Each option will be assigned to the variable options , without the leading dash. When there are no longer any arguments to process, getopts will exit with a nonzero status, causing the while loop to terminate.

  2. The case command is used to test each of the possible options found in the options variable, either x or y .

  3. If x was an option, the string you entered -x as an option is displayed.

  4. At the command line, the opts1 script is given an x option, a legal option to be processed by getopts .

  5. At the command line, the opts1 script is given an xy option; x and y are legal options to be processed by getopts .

  6. At the command line, the opts1 script is given a y option, a legal option to be processed by getopts .

  7. The opts1 script is given a b option, an illegal option. Getopts sends an error message to stderr .

  8. An option without a dash prepended to it is not an option and causes getopts to stop processing arguments.

Example 14.73.
(The Script)

    #!/bin/bash

# Program: opts2


# Using getopts  Second try

1

while getopts xy options 2> /dev/null

do

2       case $options in

        x) echo "you entered x as an option";;

        y) echo "you entered y as an option";;

3

\?) echo "Only -x and -y are valid options"  1>&2;;

esac

    done



(The Command Line)

    $

opts2 x


you entered x as an option

$

opts2 y


you entered y as an option

$

opts2 xy

$

opts2 xy


you entered x as an option


you entered y as an option

4   $

opts2 g


Only -x and -y are valid options

5   $

opts2 c


Only -x and -y are valid options


EXPLANATION

  1. If there is an error message from getopts , it is redirected to /dev/null .

  2. If the option is a bad option, a question mark will be assigned to the options variable. The case command can be used to test for the question mark, allowing you to print your own error message to standard error.

  3. If the options variable is assigned the question mark, the case statement is executed. The question mark is protected with the backslash so that the shell does not see it as a wildcard and try to perform filename substitution.

  4. g is not a legal option. A question mark is assigned to the variable options and the error message is displayed.

  5. c is not a legal option. A question mark is assigned to the variable options and the error message is displayed.

Special getopts Variables

The getopts function provides two variables to help keep track of arguments: OPTIND and OPTARG . OPTIND is a special variable that is initialized to one and is incremented each time getopts completes processing a command-line argument to the number of the next argument getopts will process. The OPTARG variable contains the value of a legal argument. See Examples 14.74 and 14.75.

Example 14.74.
(The Script)

    #!/bin/bash

# Program: opts3


# Using getopts  Third try

1

while getopts dq: options

do

case $options in

2             d) echo "d is a valid switch ";;

3             q) echo "The argument for -q is $OPTARG";;

              \?) echo "Usage:opts3 -dq filename ... " 1>&2;;

        esac

    done



(The Command Line)

4   $

opts3 d


d is a valid switch

5   $

opts3 -q  foo


The argument for -q is foo

6   $

opts3 -q


Usage:opts3 -dq filename ...

7   $

opts3 e


Usage:opts3 -dq filename ...

8   $

opts3 e


EXPLANATION

  1. The while command tests the exit status of getopts ; if getopts can successfully process an argument, it returns 0 exit status, and the body of the while loop is entered. The colon appended to the argument list means that the q option requires an argument. The argument will be stored in the special variable, OPTARG .

  2. One of the legal options is d . If d is entered as an option, the d (without the dash) is stored in the options variable.

  3. One of the legal options is q . The q option requires an argument. There must be a space between the q option and its argument. If q is entered as an option followed by an argument, the q , without the dash, is stored in the options variable and the argument is stored in the OPTARG variable. If an argument does not follow the q option, the question mark is stored in the variable options .

  4. The d option is a legal option to opts3 .

  5. The q option with an argument is also a legal option to opts3 .

  6. The q option without an argument is an error.

  7. The e option is invalid. A question mark is stored in the options variable if the option is illegal.

  8. The option is prepended with neither a dash nor a plus sign. The getopts command will not process it as an option and returns a nonzero exit status. The while loop is terminated .

Example 14.75.
(The Script)

    #!/bin/bash

# Program: opts4


# Using getopts  Fourth try

1

while getopts xyz: arguments 2>/dev/null

do

case $arguments  in

2        x) echo "you entered -x as an option .";;

         y) echo "you entered -y as an option." ;;

3        z) echo "you entered -z as an option."

             echo "$OPTARG is $OPTARG.";;

4        \?) echo "Usage opts4 [-xy] [-z  argument]"

             exit 1;;

         esac

    done

5   echo "    The number of arguments passed was $(($OPTIND - 1))"



(The Command Line)

$

opts4 -xyz foo


You entered -x as an option.


You entered -y as an option.


You entered -z as an option.


$OPTARG is foo.


The number of arguments passed was 2.

$

opts4 -x -y -z  boo


You entered -x as an option.


You entered -y as an option.


You entered -z as an option.


$OPTARG is boo.


The number of arguments passed was 4.

$

opts4 -d


Usage: opts4  [-xy] [-z argument]


EXPLANATION

  1. The while command tests the exit status of getopts ; if getopts can successfully process an argument, it returns 0 exit status, and the body of the while loop is entered. The colon appended to the z option tells getopts that an argument must follow the “z option. If the option takes an argument, the argument is stored in the getopts built-in variable OPTARG .

  2. If x is given as an option, it is stored in the variable arguments .

  3. If z is given as an option with an argument, the argument is stored in the built-in variable OPTARG .

  4. If an invalid option is entered, the question mark is stored in the variable arguments and an error message is displayed.

  5. The special getopts variable OPTIND holds the number of the next option to be processed. Its value is always one more than the actual number of command-line arguments.

14.10.2 The eval Command and Parsing the Command Line

The eval command evaluates a command line, performs all shell substitutions, and then executes the command line. It is used when normal parsing of the command line is not flexible enough for what we want to do.

Example 14.76.
1   $

set a b c d

2   $

echo The last argument is $$#

3

The last argument is

4   $

eval echo The last argument is $$#


The last argument is d

5   $

set -x

$

eval echo The last argument is $$#


+ eval echo the last argument is ''


++ echo the last argument is d


The last argument is d


EXPLANATION

  1. Four positional parameters are set.

  2. The desired result is to print the value of the last positional parameter. The \$ will print a literal dollar sign. The $# evaluates to 4 , the number of positional parameters. After the shell evaluates the $# , it does not parse the line again to get the value of $4 .

  3. $4 is printed instead of the last argument.

  4. After the shell performs variable substitution, the eval command performs the variable substitution and then executes the echo command.

  5. Turn on the echoing to watch the order of parsing.

Example 14.77.
(From Shutdown Program)

1

eval `/usr/bin/id  /usr/bin/sed 's/[^a-z0-9=].*//'`

2   if [ "${uid:=0}" -ne 0 ]

    then

3       echo
(From Shutdown Program) 1

eval `/usr/bin/id  /usr/bin/sed 's/[^a-z0-9=].*//'`

2 if [ "${uid:=0}" -ne 0 ] then 3 echo $0: Only root can run $0 exit 2 fi
: Only root can run
(From Shutdown Program) 1

eval `/usr/bin/id  /usr/bin/sed 's/[^a-z0-9=].*//'`

2 if [ "${uid:=0}" -ne 0 ] then 3 echo $0: Only root can run $0 exit 2 fi
exit 2 fi

EXPLANATION

  1. This is a tricky one. The id program's output is sent to sed to extract the uid part of the string. The output for id is

    uid=9496(ellie) gid=40 groups=40
    
    uid=0(root) gid=1(daemon) groups=1(daemon)
    

    The sed regular expression reads: Starting at the beginning of the string, find any character that is not a letter, number, or an equal sign and remove that character and all characters following it. The result is to substitute everything from the first opening parenthesis to the end of the line with nothing. What is left is either uid=9496 or uid=0.

    After eval evaluates the command line, it then executes the resulting command: uid=9496 or uid=0 .

    For example, if the user's ID is root , the command executed would be uid=0 . This creates a local variable in the shell called uid and assigns 0 to it.

  2. The value of the uid variable is tested for 0, using command modifiers.

  3. If the uid is not 0, the echo command displays the script name ( $0 ) and the message.

 <  Day Day Up  >