Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Basic Control Statements

The examples up to this point have been very basic, linear programs. This section will introduce some control flow statements to allow for more complex programs.

Note: C shell programming is not covered by this section in detail. The syntax for control statements in C shell programming is essentially the same as in C except for the following caveats:

For more information, see the manual page for csh.

In this section:

The if Statement
The test Command And Bracket Notation
The while Statement
The for Statement
The case statement
The expr Command


The if Statement

The first control statement you should be aware of in shell scripting is the if statement. This statement behaves very much like the if statement in other programming languages, with a few subtle distinctions.

The first distinction is that the test performed by the if statement is actually the execution of a command. When the shell encounters an if statement, it executes the statement that immediately follows it. Depending on the return value, it will execute whatever follows the then statement. Otherwise, it will execute whatever follows the else statement.

The second distinction is that in shell scripts, many things that look like language keywords are actually programs. For example, the following code executes /bin/true and /bin/false.

# always execute
if true; then
    ls
else
    echo "true is false."
fi
# never execute
if false; then
    ls
fi

In both of these cases, an executable is being run—specifically, /bin/true and /bin/false. Any executable could be used here.

A return of zero (0) is considered to be true (success), and any other value is considered to be false (failure). Thus, if the executable returns zero (0), the commands following the then statement will be executed. Otherwise, the statements following the else clause (if one exists) will be executed.

The reason for this seemingly backwards definition of true and false is that most UNIX tools exit with an exit status of zero upon success and a non-zero exit status on failure, with positive numbers usually indicating a user mistake and negative numbers usually indicating a more serious failure of some sort. Thus, you can easily test to see if a program completed successfully by seeing if the exit status is the same as that of true.

One related statement that you should be familiar with is elif. This is similar to saying else if except that it does not require an additional fi at the end of the conditional, and thus results in more readable code.

For example:

#/bin/sh
 
read A
if [ "x$A" = "xfoo" ] ; then
    echo "Foo"
elif [ "x$A" = "xbar" ] ; then
    echo "Bar"
else
    echo "Other"
fi

This example reads a string from standard input and prints one of three things, depending on whether you typed “foo”, “bar’, or anything else. (The bracket syntax used in this example is explained in the next section, “The test Command And Bracket Notation.”)

The test Command And Bracket Notation

While the if statement can be used to run any executable, the most common use of the if statement is to test whether some condition is true or false, much like you would in a C program or other programming language. For example, the if statement is commonly used to see if two strings are equal.

Because the if statement runs a command, in order to use the if statement in this fashion, you will need a program to run that performs the comparison desired. Fortunately, one is built into the OS: test. The test executable is rarely run directly, however. Generally, it is invoked by running [, which is just a symbolic link or hard link to /bin/test.

Note: While the open bracket is a command, and there is a man page, you will have a hard time getting to it on the command line. Use:

man \\[
to see it (or just look at the man page for test).

In this form, the syntax of an if statement more closely resembles other languages. Consider the following example:

#! /bin/sh
 
FIRST_ARGUMENT="$1"
if [ x$FIRST_ARGUMENT = "xSilly" ] ; then
    echo "Silly human, scripts are for kiddies."
else
    echo "Hello, world $FIRST_ARGUMENT!"
fi

There are two things you should notice. First, the space before the equals sign is critical. This is the difference between assignment (no space) and comparison (space). The spaces around the brackets are also critical, as failure to include these spaces will result in a syntax error. (Remember, the open bracket is really just a command, and it expects that its last argument will be a close bracket by itself.)

Second, you should notice that the two arguments to the comparison are preceded by an ‘x’. The reason for this is that the variable substitution occurs before this statement is executed. If you omit the ‘x’ and the value in $FIRST_ARGUMENT is empty this would evaluate to “if [ = "Silly" ]”, which would be a blatant syntax error.

Another way to solve the empty variable problem is through the use of double quote marks. That way, even if the variable is empty, there is a placeholder. The following example uses double quote marks to test to see if a variable is empty:

if [ "$VARIABLE" = "" ] ; then
    echo "Empty variable \$VARIABLE"
fi

Now this example introduces another special character, the backslash. This is also known as a quote character because the character immediately after it is treated as though it were within quotes. Thus, in this case, the snippet prints the name of the variable $VARIABLE rather than its contents. This is described further in “Quoting Special Characters.”

The test command can also be used for various other tests, including the existence of a file, whether a path points to a directory, an executable, or a symbolic link, and so on. For example:

if [ -d "/System/Library/Frameworks" ] ; then
    echo "/System/Library/Frameworks is a directory."
fi

A complete list of switches for the test command can be found in the man page test.

The while Statement

In addition to the if statement, the Bourne shell also supports a while statement. Its syntax is similar.

while true; do
    ls
done

Like the if statement’s then and fi, the while statement is bracketed by do and done. Much like the if statement, the while statement takes a single argument, which contains a command to execute. Thus, it is common to use the bracket command with while just as you would with if. For example:

while [ "x$FOO" != "x" ] ; do
    FOO="$(cat)";
done

Of course, this is a rather silly example. However, it does demonstrate one of the more powerful features in the Bourne shell scripting language: the $() operator, which inserts the output of one command into the middle of a statement. In the case above, the cat command is executed, and its standard output is stored in the variable FOO. This is described more in “Inline Execution.”

The for Statement

The most unusual control structure in this chapter is the for statement. The for statement in shell scripts is completely unlike its C equivalent (which requires numeric computation, as described in “Paint by Numbers”), and actually behaves much like the foreach statement in various languages. It iterates through each of the items in a list. In the next example, the list is *.JPG, which the shell replaces with a list of files in the current directory that end in .JPG.

Without going into details about the regular expression syntax used by the sed command (this is described in more detail in “Regular Expressions Unfettered”), the following script renames every file in the current directory that ends with .JPG to end in .jpg.

#!/bin/sh
for i in *.JPG ; do
    mv "$i" "$(echo $i | sed 's/\.JPG$/.x/')"
    mv "$(echo $i | sed 's/\.JPG$/.x/')" "$(echo $i | sed 's/\.JPG$/.jpg/')"
done

The for statement (by default) splits the file list on unquoted spaces. For example, the following script will print the letters “a” and “b” on separate lines, then print “c d” on a third line:

#!/bin/sh
for i in a b c\ d ; do
    echo $i
done

Under certain circumstances, you can change the way that the for statement splits lists by changing the contents of the variable IFS. The details of when this does and does not work are described in “Variable Expansion and Field Separators.”

The case statement

The final control statement in this chapter is the case statement. The case statement in shell scripts is similar to the C switch statement. It allows you to execute multiple commands depending on the value of a variable. The syntax is as follows:

case expression in
    [(] value | value | value | ... ) command; command; ... ;;
    [(] value | value | value | ... ) command; command; ... ;;
    ...
esac

You should notice three things about this syntax. First, each case is terminated by a double semicolon. Second, the opening parenthesis is optional and is frequently dropped by script authors. Third, a single set of commands can be applied to any number of values separated by the pipe (vertical bar) character (|).

For example, the following code sample prints the English names for the numbers 0–9, then prints them again.

#!/bin/sh
 
LOOP=0
 
while [ $LOOP -lt 20 ] ; do
        # The next line is explained in the
        # math chapter.
        VAL=`expr $LOOP % 10`
 
        case "$VAL" in
                ( 0 ) echo "ZERO" ;;
                ( 1 ) echo "ONE" ;;
                ( 2 ) echo "TWO" ;;
                ( 3 ) echo "THREE" ;;
                ( 4 ) echo "FOUR" ;;
                ( 5 ) echo "FIVE" ;;
                ( 6 ) echo "SIX" ;;
                ( 7 ) echo "SEVEN" ;;
                ( 8 ) echo "EIGHT" ;;
                ( 9 ) echo "NINE" ;;
                ( * ) echo "This shouldn't happen." ;;
        esac
 
        # The next line is explained in the
        # math chapter.
        LOOP=$((LOOP + 1))
done

You should notice the ( * ) case at the end. This is the equivalent of the default case in C. While that case will never be reached in this example, if you change the value of the modulo from 10 to any larger value, you will see that this case executes when no previous case matches the value of the expression.

The expr Command

No discussion of tests and comparisons would be complete without mentioning the expr(1) command. This command can perform various string comparisons and basic integer math. The math portions of the expr command are described in “The expr Command Also Does Math.”

The expr command is fairly straightforward. Each expression or token passed to the command must be surrounded by quotes if it may contain multiple words or characters that the shell considers special. For example, to compare two strings alphabetically, you could use the following command:

expr "This is a test" '<' "I am a person"

The following version would fail miserably because the shell would interpret the less-than sign as a redirect and would try to read from a file called “I am a person”:

expr "This is a test" < "I am a person"

The details of quoting are described further in “Variables, Expansion, and Quoting.”

The expr command supports the usual complement of string comparisons (equality, inequality, less-than, greater-than, less-than-or-equal, and greater-than-or-equal).

In addition to these comparisons, the expr command can do several other tests: a logical “or” operator, a logical “and” operator, and a (fairly limited) basic regular expression matching operator.

The “or” operator (|) prints the first expression ("$1") if it is nonempty and contains something other than the number zero (0). Otherwise, if the second string is nonempty and contains something other than the number zero, it prints the second expression ("Untitled"). If both strings are empty or zero, it prints the number zero.

You can use the “or” operator to substitute a default string using the or operator like this:

#!/bin/sh
 
NAME=`expr "$1" '|' "Untitled"`
echo "The chose name was $NAME"

Note: Because the expr command does not distinguish between the number zero (0) and an empty string, you should not use expr to test for an empty string if there is a possibility that the string might be "0".

The “and” operator (&) is similar, returning either the first string (if both strings are nonempty) or zero (if either string is empty).

Finally, the expr command can work with basic regular expressions (not extended regular expressions) to a limited degree.

To count the number of characters from the beginning of the string (all expressions are implicitly anchored to the start of the string) up to and including the last letter ‘i’, you could write an expression like this:

STRING="This is a test"
expr "$STRING" : ".*i"

If the string does not match the expression, the expr command returns zero (0), which corresponds with the number of characters matched.

The most common use for this syntax is obtaining the length of a string, as shown in this snippet:

STRING="This is a test"
expr "$STRING" : ".*"

This same syntax can be used to return the text captured by the first set of parentheses in a basic regular expression. For example, to print the four characters immediately prior to the last occurrence of “est”, you could write an expression like this one:

STRING="This is a test" expr "$STRING" : '.*\(....\)est'

Because this expression contains capturing parentheses, if the first string does not match the expression, the expr command returns an empty string.

For more information about writing basic regular expressions, read “Regular Expressions Unfettered.”



< Previous PageNext Page > Hide TOC


Last updated: 2008-04-08




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice