Flow Control

  1. Unix Shell Scripting
  2. Shell Basics
  3. Testing in bash
  4. Capturing User Input
  5. Scripting : Exercise 1
  6. Debugging
  7. Bourne/Bash/Korn Commands
  8. Shell Variables
  9. IO Redirection
  10. Pipes
  11. Operators, Wildcards and Expressions
  12. Flow Control
  13. Scripting : Exercise 2
  14. Shell Differences
  15. String Functions
  16. awk
  17. xargs
  18. Power Tools
  19. Exercise 3

The Bourne/bash shell supports a variety of conditionals, loops and other flow control operations. You’ll use these often.


The if statement is a simple conditional. Its syntax is:

if condition ; then
[elif condition ; then

This is an if-block, optionally followed by one or more elif-blocks (elif is short for “else if”), optionally followed by an else-block, and terminated by fi.

The if statement does what you’d expect: if the condition is true, it executes the if-block. Otherwise, it executes the else-block, if there is one. The elif construct lets you avoid nesting multiple if statements. For instance:

if [ $USER = root ]; then
echo "Welcome to FooSoft 3.0"
echo "You must be root to run this script"
exit 666

Notice the square brackets in the condition statement. Actually, only the left bracket is important, because it is aliased to the test command.

The condition can actually be any command. If it returns a zero exit status, the condition is true; otherwise, it is false. Thus, you can write things like:


mytest=`grep $user /etc/passwd|wc -l`
# Notice the backticks, not single quotes, above
if [ $mytest -gt 0 ]; then
echo "$user has an account"
echo "$user doesn't have an account"

Create a “password” file named passwords, with two columns: one holding a user name, and the second holding a user password. There should be at least three name/password pairs.

Use the script above to model a test: that the user at least exists in the password file. Name your script passcheck.sh.

Test and run it.


The for loop iterates over all of the elements in a list. Its syntax is

for var in list

The element list is zero or more words (elements). The for construct will assign the variable var to each word in turn, then execute commands. For example, the elements can be listed directly:

for x in red green blue
echo $x

Which will return:


Notice that spaces define individual strings. To deal with a string that contains spaces, enclose it in weak quotes (“).

for i in foo bar baz "do be do"; do
echo "$i"

This will print:

do be do

Note that if some of the elements will return with embedded spaces, you need to protect them with quotes.

color1="red chile"
color2="green chile"
for x in "$color1" "$color2" "$color3"
echo "$x"

Here’s the point: variables should be protected with quotes unless you are sure that their value does not contain any spaces.

The elements in a for loop need not be listed separately. They can instead be supplied by a command.

for x in $(ls)
echo "$x"

A for loop may contain two special commands: break and continue. break exits the for loop immediately, jumping to the next statement after done. continue skips the rest of the body of the loop, and jumps back to the top, to for.


#remove spaces in file names
for i in *.wma; do mv "$i" `echo $i | tr ’ ’ ’_’`; done

See http://www.linux-mag.com/id/8797/ for an excellent example of for loop usage.


The while statement should also be familiar to you from any number of other programming languages. Its syntax in sh is

while condition

The while loop executes commands as long as the condition is true. Again, the condition can be any command, and is true if the command exits with a zero exit status (in other words, there wasn’t an error). Consider a simple mathematical example:

# set the variable
# test: less than or equal to 20
while [ $x -le 20 ]
# begin the actual loop
# notice that the do command could have been
# on the previous line using the in-line return
# character " ; "
echo $x
# increment x by 1

A while loop may contain two special commands: break and continue.

break exits the while loop immediately, jumping to the next statement after done.

continue skips the rest of the body of the loop, and jumps back to the top, to condition

Using Command Output to Supply the Elements of a Loop

The elements in a for loop need not be listed separately. They can instead be supplied by a command. Try this loop:

for x in `ls`
echo $x
  1. What did you get?
  2. Now substitute `who` for the `ls` command.
  3. Create a file listing the contents of your home directory:
    ls ~ > files
  4. How can you make this file supply the list for the for loop?


An interesting thing happens when you include a filename wildcard character in a command. Remember that the shell “gets to” the wildcards first, and expands them. By default, wildcards (to the shell) are always references to filenames, unless some other comparison is specified. This is known as “globbing.” Globbing is used mainly in case and for statements.

The shell expands a string containing a * to all filenames that match. The character * by itself expands to a space -delimited list of all files in the working directory (excluding those that start with a dot “.” ).

When a glob begins with * or ?, it does not match files that begin with a dot. To match these, you need to specify the dot explicitly (e.g., .* or /tmp/.*). Under DOS, the pattern *.* matches every file. In sh, it matches every file that contains a dot.


echo *

lists all the non-hidden files and directories in the current directory.

echo *.jpg

lists all the jpeg files.

echo ${HOME}/public_html/*.jpg

lists all jpeg files in your public_html directory.

As it happens, this turns out to be very useful for performing operations on the files in a directory, especially used in conjunction with a for loop. For example:

for x in ${HOME}/public_html/*.htm
grep -L '<bold>' "$x"


The case command’s syntax is:

case value in
commands ;;
#commands are executed until a double-semicolon is hit
commands ;;

value is a string; this is generally either a variable or a back quoted command.

Notice that a case block ends with esac.

pattern is a glob pattern (see globbing, above). For instance,

case pattern in
gle*) command ;;
glen*) command ;;
fred) command ;;
barney) command ;;

The patterns are evaluated in the order in which they occur, and only the first pattern that matches will be executed. To include a “none of the above” clause, use * as your last pattern.

case "$color" in
echo \$color is blue
echo \$color is green
echo \$color is red or orange
*) echo "Not a match"

The “|” is used to separate multiple patterns; it functions as a logical “or.”

echo -n "Enter the name of an animal: "
echo -n "The $ANIMAL has "

case $ANIMAL in
horse | dog | cat) echo -n "four";;
man | kangaroo ) echo -n "two";;
*) echo -n "an unknown number of";;
echo "legs."