Shell Script Conditional and Repetitive Execution Features

You know that shell scripts let you batch a number of individual Unix commands together into a file and execute them collectively in sequence. Beyond the commands themselves, you can add program flow control with the same kinds of constructs found in programming languages: conditional execution and repetition. This elevates shell scripting to the level of programming.

Conditional Evaluation ( if )

Conditional evaluation means the ability to select part of a program and not necessarily execute it. Without conditional execution, you necessarily do execute all parts of a program because the way programs work is to run the lines of code written by the programmer in order top to bottom no questions asked period thank you very much. With conditional evaluation you select a piece of the program (e.g., lines 45 through 70) and set things up so that whether you'll execute it depends on something. You might execute it, then again you might not-- it depends. The way to separate the desired piece is to surround it with 2 special statements, usually some form of the word "if" plus something like "end," or "endif" or in the case of Unix, with "fi". (It's fi because fi is if spelled backwards.) This is the standard form of a Unix shell script if construct.

if condition
then
  statements
fi

Expressing the condition properly can be a bit tricky. You have to write an "expression" there. Expressions have results. The result of an expression is called an "exit status." You can explore this briefly right after you execute any command, because the command has an exit status. Usually the exit status doesn't appear anywhere, it's invisible. But it's represented by a standard variable with the curious name $? So if you examine $? right after running any command you'll be able to see its exit status. You examine it with the command "echo $?". The exit status is a number. If the command succeeds it's usually 0. But if the exit status is any other number it means the command failed in one way or another. For example, after you execute "ls -l" if you type "echo $?" a 0 appears on the screen. But try to deliberately use an illegal option like -p (ls has no -p option) by executing "ls -p" and you'll see some kind of error message. Right then, type "echo $?" and a 1 will appear. Alternatively, deliberately misspell the command lz instead of ls. An error message. At that point "echo $?" reveals an exit status of 127. Both 1 and 127 indicate failure because they are both nonzero.

How does this apply in an if statement? You put an expression in the if's condition. It will be "executed," or more accurately evaluated, something like a command is at the command line. And it, too, internally produces an exit status number. That's what the if looks at to decide whether or not to do the "statements". If the condition produces 0 as its exit status, it's true, and the statements do get executed. But if the condition produces any other number as exit status, it's considered false and the statements get skipped instead.

You can actually place a regular command in the "condition." More often, a special command called "test" is used. It's made for producing exit statuses of certain kinds of expressions that you give it. The expressions can be categorized as string comparisons, arithmetic comparisons, and file conditionals. For each, there is a special way of expressing conditions:

String comparisons

Expression:         True if:

string              string is not an empty string
-z string           string is an empty string
string1 = string2   strings are same
string1 != string2  strings are not same

 

Arithmetic comparisons

Expression:         True if:

exp1 -eq -exp2      expressions equal
exp1 -ne -exp2      expressions not equal
exp1 -gt -exp2      exp1 greater than exp2
exp1 -lt -exp2      exp1 less than exp2
! expression        expression is false

 

File conditionals

Expression:         True if:

-e file             file exists
-d file             file is a directory
-f file             file is a regular file
-r file             file is a readable
-w file             file is a writeable
-x file             file is a executable

You insert an expression with one of the above forms into the "condition" of an if statement. For example:

if test -f fred.c
then
  echo fred is a friendly fellow
fi

will be true if fred.c exists and is a regular file (not a directory or something exotic). In that case, the user will see "fred is a friendly fellow" printed by echo on the screen. Otherwise, nothing happens to the screen.

A shorthand form of "test" used often is to replace the word with a pair of surrounding square brackets, like this:

if [ -f fred.c ]
then
  echo fred is a friendly fellow
fi

This works just the same way. The only caution is that you must leave whitespace (just hit the spacebar at least once) after the opening bracket and before the closing one. In other words don't write [-f fred.c] instead of [  -f fred.c  ]. It won't work, and you won't know why. It drives beginning shell script programmers crazy. Here's another example:

echo "Is it morning? Answer yes or no"
read timeofday
if [ $timeofday = "yes" ];then
  echo "Good morning"
else
  echo "Good afternoon"
fi

(Note you can put the "then" on the same line as the "if" if you supply a semicolon.)

Repetition ( for, while )

There are different ways to execute a piece of your script multiple times. You surround it with "do" and "done," and precede the whole thing with "for" or "while." Each works a little differently. 

The "for" statement uses a variable and a list of values. It puts the values in the list into the variable one at a time, executing the statements it surrounds once for each.

for variable in values
do
  statements
done

Below, the list has 3 values, so the surrounded code (the "echo") runs 3 times. It will print the changing value of for's variable, foo, each time. Why? The dollar sign before a variable means, "print the value contained in this variable." So this loop will print "bar," then "fud," and then "43."

for foo in bar fud 43
do
  echo $foo
done

Similarly, the following works the same way except that the values are auto-generated by a command. In this case, the ls command. It will give all the filenames that start with lab. If there are 2 of them , the loop (surrounded code) will run twice. If 5, five times; if 10, ten times. Each time, it will print the name of one such file. 

for file in `ls lab*`
do
  echo $file
done

Unlike for, while does not execute the surrounded code a number of times that is predetermined. Here is it's general form:

while condition do
  statements
done

 It's unknown when you begin running that code just how many times it's going to ultimately. For example, the following will run as many time as the user fails to enter the right word, "secret." As soon as he does however, the loop will stop. This is in effect a simple (insecure) password test program. 

read trythis
while [ "$trythis" != "secret" ]
do
  echo "Sorry, try again"
  read trythis
done

On the remote Unix server you can find these and similar examples. They are in directory /home/public/class/shellprogramming. Please examine and experiment with the scripts named if1, if2, for1, for2, and while1. Trying to run them will make things much clearer than just reading about them.