Conditional Execution


An if statement tests whether a given condition is true. If it is, the block of code within the if statement will be executed. This is the general form of an if statement:

 if testcommand then     command(s) fi 

The command following the keyword if is executed. If it has a return value of zero (true), the commands following the keyword then are executed. The keyword fi (if spelled backward) marks the end of the if structure. Although the indentation of the commands does not have any effect when the script is executed, it can make a tremendous difference in making your scripts more readable.

UNIX System commands provide a return value or exit status when they complete. By convention, an exit status of zero (true) is sent back to the original process if the command completes normally; a nonzero exit status (false) is returned otherwise. This can be used as the test condition in an if statement. For example, you might want to execute a second command only if the first completes successfully Consider the following lines:

 # Copy the directory $WORK to ${WORK}.OLD cp -r $WORK ${WORK}.OLD # Remove $WORK rm -r $WORK

The problem with this sequence is that you would only want to remove the $WORK if it has been successfully copied. Using ifthen allows you to make the rm command conditional on the outcome of cp. For example,

 # Copy the directory $WORK to ${WORK}.OLD # Remove $WORK if copy is successful if cp -r $WORK ${WORK}.OLD then    rm -rf $WORK fi

In this example, $WORK is removed only if cp completes successfully and sends back a true (zero) return value. The -f option to rm suppresses any error messages that might result if the file is not present or not removable.

Testing Logical Conditions

You often need to test conditions other than whether a command was successful. The test command can be used to evaluate logical expressions in your if statements. When test evaluates a true expression, it returns 0. If the expression is false (or if no expression is given), test returns a nonzero status.

test allows you to compare integers or strings. The test -eq form checks to see if two integers are equal. For example, you could check the number of arguments that had been provided to a script:

 if test $# -eq 0 then      echo "No command line arguments provided, setting user to current user."      username=$LOGNAME fi

If $# is equal to zero (meaning there were no command-line arguments), the message is displayed and the variable username is set. Otherwise, the script continues after the keyword fi.

Table 20–1 shows the tests allowed on integers.

Table 20–1: Integer Tests

Integer Test

True If

n1 -eq n2

n1 is equal to n2

n1 -ne n2

n1 is not equal to n2

n1 -gt n2

n1 is greater than n2

n1 -ge n2

n1 is greater than or equal to n2

n1 -It n2

n1 is less than n2

n1 -le n2

n1 is less than or equal to n2

Similarly, you can use test to examine strings, although the syntax is a bit different than for integers. For example,

 if test -z "$input" then input="default" fi

checks to see if the length of $input is zero, and if so, it sets the value to “default”. Including the quotes around $input prevents errors when the variable is undefined (because even when $input is undefined, “$input” has the value “”).

Table 20–2 shows the tests you can use on strings.

Table 20–2: String Tests

String Test

True if

-z string

length of string is zero

-n string

length of string is nonzero

string

string is not the null string (same as -n)

string1=string2

string1 is identical to string2

string1 != string2

string1 is not identical to string2

In some cases, you may want to test a more complex logical condition. For example, you might want to check if a string has one of two different values, as in this example:

 if test "$input" = "quit" -o "$input" = "Quit"   then exit 0 fi

The operator -o stands for or. It returns the value true if the first condition or the second condition (or both) is true. Here’s a rather complex example with logical operators:

 if test ! \( $x -gt 0 -a $y -gt 0 \)   then echo "Both x and y should be greater than 0." fi

This uses the operator ! to stand for not, and -a for and. It says “if it is not the case that both $x is greater than 0 and $y is greater than 0, then print the error message.” Parentheses are used to group the statements. If the parentheses were removed, it would say “if it is not the case that $x is greater than 0, and it is the case that $y is greater than 0, print the error.” In order to prevent the shell from interpreting them, the parentheses must be quoted with \.

Table 20–3 lists the logical operators in sh.

Table 20–3: Logical Operators

Operator

Meaning

!

Negation

-a

AND

-o

OR

Using Brackets for Tests

Surrounding a comparison with square brackets has the effect of the test command. The brackets must be separated by spaces from the text, as in

 if [ $# -eq 0]

If you forget to include the spaces, as in [$# -eq 0], the test will not work.

Here are some sample test expressions, and the equivalents using square brackets:

test $ # -eq 0

# Same as

[ $# -eq 0]

test -z $1

# Same as

[ -z $1]

test $1

# Same as

[$1]

Tests in ksh and bash

The shells ksh and bash provide the operator [[ ]], which can be used as another alternative to test. If the positional parameter $1 is set, the following three tests are equivalent:

 test $1 = turing [ $1 = turing] [[ $1 = turing ]]

However, if $1 is not set, the first two versions of the test will give you an error, but the double bracket form will not.

The [[ ]] operator allows you to use the expression && for AND and | | for OR. It also understands < and > when comparing numbers. This can make your conditions significantly easier to type and read. For example, in ksh and bash, the following line says “it is not the case that both $x and $y are greater than zero”:

 [[ ! ( $x > 0 && $y > 0)]]

Whereas with test it would look like this:

 test ! \( $x -gt 0 -a $y -gt 0 \)

Testing Files and Directories

You can also evaluate the status of files and directories in your if statements. For example,

 if [ -a "$1"]

checks to see if the first argument to the script is a valid filename. Checking to see if files exist is very common in shell scripts. As in this example, you will often want to check that filename arguments are valid before trying to run commands on them.

Table 20–4 shows the most common tests for files and directories.

Table 20–4: Tests for Files and Directories

File Tests

True if...

-a file

file exists

-r file

file exists and is readable

-w file

file exists and is writable

-x file

file exists and is executable

-f file

file exists and is a regular file

-d file

file exists and is a directory

-h file

file exists and is a symbolic link

-c file

file exists and is a character special file

The following example shows how you could check that a file exists before mailing it. If the file exists and is bigger than zero, the script mails it to $LOGNAME. If mail completes successfully, the file is removed.

 if test -s logfile$$ then     if mail $LOGNAME < logfile$$     then         rm -f logfile$$     fi fi

Exiting from Scripts

The built-in shell command exit causes the shell to exit and return an exit status number. By convention, an exit status of 0 (zero) means the program terminated normally, and a nonzero exit status indicates that some kind of error occurred. Often, an exit value of 1 indicates that the program terminated abnormally (because the user interrupted it with CTRL-C, e.g.), and an exit value of 2 indicates a usage or command-line error by the user. If you specify no argument, exit returns the status of the last command executed.

The exit command is often found inside a conditional statement. For example, this script will exit if the first command-line argument is not a valid filename.

 if [ ! -a "$1"] then     echo "File $1 not found."     exit 2 fi

if... elif else Statements

The if ... elif ... else operation is an extension of the basic if statements just shown. It allows for more flexibility in controlling program flow. The general format looks like this:

 if testcommand then      command(s) elif testcommand then      command(s) else      command(s) fi 

The command following the keyword if is evaluated. If it returns true, then the commands in the first block (between then and elif) are executed. If it returns false, however, then the command following elif is evaluated. If that command returns true, the next block of commands is executed. Otherwise, if both test commands were false, then the last block (following else) is executed. Note that, regardless of how the test commands turn out, exactly one of the three blocks of code is executed.

Because if ... elif ... else statements can be quite long, the examples here show the keyword then on the same line as the test commands. This can make your scripts more readable, although it is entirely a question of personal style. Notice, however, that a semicolon separates the test commands from the then. This semicolon is required so that the shell interprets then as a new statement and not as part of the test command.

Here’s an example that just uses the if and else blocks, without elif.

 if [ -a "$1"] ; then     # good, the argument is a file that exists     inputfile = $1 else     # print error and exit     echo "Error: file not found"     exit 1 fi

This could be expanded with an elif block:

 if [ -a "$1"] ; then     # good, the argument is a file that exists     # we can assign it to a variable     # and continue after the keyword fi     inputfile = $1 elif [ ! $1] ; then     # the argument $1 isn't defined     # print error message and exit     echo 'Error: filename argument required"     exit 1 else     # the problem must be that the file doesn't exist     # print error and exit     echo "Error: file $1 not found"     exit 1 fi

case Statements

If you need to compare a variable against a long series of possible values, you can use a long chain of if ... elif ... else statements. However, the case command provides a cleaner syntax for a chain of comparisons. It also allows you to compare a variable to a shell wildcard pattern, rather than to a specific value.

The syntax for using case is shown here:

 case string in pattern)         command(s)         ;; pattern)         command(s)         ;; esac 

The value of string is compared in turn against each of the patterns. If a match is found, the commands following the pattern are executed up until the double semicolon (;;), at which point the case statement terminates. If the value of string does not match any of the patterns, the program goes through the entire case statement.

Here’s an example of a case statement. It checks $INPUT to see if it is a math statement containing +, , *, or /. If it is, the statement is evaluated with bc. If $INPUT says “Interactive”, the script runs a copy of bc for the user. If $INPUT is a string such as “quit”, the script exits. And if it is something else, the script prints a warning message.

 case $INPUT in *+* | *..* | *\** | */*)       echo "scale=5; $INPUT" | bc       ;; "Interactive")       echo "Starting bc for interactive use."       echo -e "Enter bc commands. \c"       echo "To quit bc and return to this script, type quit."       bc       echo "Exiting bc, returning to $0."       ;; [Qq]uit | [Ee]xit)       # matches the strings Quit, quit, Exit, and exit       echo "Quitting now."       exit 0       ;; *)       echo "Warning: input string does not match."       ;; esac

In this case statement, the * in the last block matches any string, so this block is executed by default if none of the other patterns match.

Note for C programmers: unlike the break statement in C, the ;; is not optional. You cannot leave out the ;; after a block of commands to continue executing the case statement after a match.




UNIX. The Complete Reference
UNIX: The Complete Reference, Second Edition (Complete Reference Series)
ISBN: 0072263369
EAN: 2147483647
Year: 2006
Pages: 316

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net