Flow control statements

if statement

The general syntax of the if statement is
if test_command
  then
    commands
 [else
    commands
 ]
fi
The if statement tests the status returned by the test_command and transfer controls based on that status. The returned status considered true is the error code returned by the test_command is 0, and it's considered to be false if it's not 0. It can be any command that returns a value to the shell, but most often the special built-in command test is used.

The test command

The test command is a built-in BASH command, or an external command in other shells. The test command evaluates a logical expression passed through arguments. This commands allows to evaluate the logical expressions presented in the table and some others (see help about the test command).
Expression Returns true when
Compare strings
[-n] STRING the length of STRING is nonzero
-z STRING the length of STRING is zero
STRING1 = STRING2 the strings are equal
STRING1 != STRING2 the strings are not equal
Compare integers
INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2
INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2
INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2
INTEGER1 -le INTEGER2   INTEGER1 is less than or equal to INTEGER2
INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2
INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2
Test files
FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2
FILE1 -ot FILE2 FILE1 is older than FILE2
-d FILE FILE exists and is a directory
-e FILE FILE exists
-f FILE FILE exists and is a regular file
-L FILE FILE exists and is a symbolic link
-r FILE FILE exists and is readable
-s FILE FILE exists and has a size greater than zero
-w FILE FILE exists and is writable
-x FILE FILE exists and is executable
Here are several examples:
if test $# -eq 0
  then
    echo "You have to provide at least one argument!"
	exit
fi
In this example please pay attention to the new line after the 0 in the test command. We cannot put then on the same line, because if we do it'll be treated as an argument of the test command. In other words, we need to separate the test commands with its arguments from the then keyword. To do that we need to either put a new line or a semicolon symbol, which also separates one command from the other.
if test -f "$1"; then
    echo "$1 is a regular file in the working directory"
  else
    echo "$1 is NOT a regular file in the working directory"
fi
We can also use combinations of logical conditions using logical operators AND (option -a) and OR (option -o)
if test $# -gt 0 -a -r "$1"
  then
    echo File '$1' has `cat $1 | wc -l` lines 
fi
We can also use parenthesis for difficult expressions and logical NOT, we need to use exclamation mark for this one. Note: Beware that parentheses need to be escaped (e.g., by back­slashes) for shells.
if test $# -gt 0 -a \( -f "$1" -o -d "$1" \) ; then
  echo "$1 is a regular file or a directory"
fi
There is a shortcut for the test command. Instead of using test with the expression as an argument we can put the same expression inside the square brackets. It looks more like usual languages
if [ $# -gt 0 -a \( -f "$1" -o -d "$1" \) ]; then
  echo "$1 is a regular file or a directory"
fi
Please note that the brackets must be surrounded by spaces or tabs.

case statement

The case structure is a multiple-branch mechanism. The general syntax of the case statement is
case test_string in
  pattern_1) 
    commands_1
    ;;
  pattern_2)
    commands_2
    ;;
  ...
  pattern_n)
    commands_n
    ;;
esac
where patterns is an expression that may contain the following special symbols
Symbol Description
* Matches any string of characters.
? Matches any single character.
[...] Defines a character set. Any character enclosed within brackets are tried, one at a time, in an attempt to match a single character. A hyphen between two characters specifies a range of characters.
| Separates alternative choices that satisfy a particular branch of the case statement.
The typical example is
 
case "$1" in
  start)
    echo "Starting the program"
    ;;
  stop)
    echo "Stopping the program"
    ;;
  restart)
    echo "Restarting the program"
    ;;
  *)
    echo -e "Use:\n\t$0 start|stop|restart"
    ;;
esac

Loops

There are several constructions in the bash shell that allow programmers to repeat a command sequence.

for...in

The general syntax of the for...in statement is
for loop_index in argument_list
do
  commands
done
This construction assigns the value of the first argument in the argument_list to the variable loop_index and executes the commands between do and done. Then it assigns the next value in the list ..., etc. Example:
# dirlist
for fl in *
do
  if [ -d $fl ]; then
    echo $fl
  fi
done
We can also assign the list manually:
num=1
echo Your choice:
for item in file edit search format insert tools tags options help
do
  echo "  $num: $item"
  num=`expr $num + 1`
done
read choice 
or run the same commands for each script argument
for a in $@
do
 echo $a
done

for

The for control structure is the shorter version of the last example. All it does it takes one the value of each of the command line arguments and puts it onto the loop_index variable. The general syntax of this structure is
for loop_index
do
  commands
done

We can also use C-like syntax with the for loop:

N=10
for((i=0;i<N;i++))
do
  echo $i
done
Please pay attention that we must have double parenthesis and we do not need a leading dollar sing with N.

while and until

There are two logical loops. There syntax is
while test_command
do
  commands
done
and
until test_command
do
  commands
done
The while loop keeps repeating the commands while the test_command returns true value (that is, zero error code), and the until loop keeps repeating while the test_command returns false; that is, it works until it becomes true.

Let's take a look at the useful script locktty from the A Practical Guide to Red Hat Linux 8 by Mark Sobell.

#!/bin/bash
trap '' 1 2 3 18
stty -echo
echo -n "Your keyword: "
read kw_one
echo
echo =n "Confirmation: "
read kw_two
if [ "$kw_one" = "$kw_two" ]; then
  kw_two=
  tput clear
  until [ "$kw_one" = "$kw_two" ]
  do
    echo -e "\nEnter the keyword to activate: \c"
    read kw_two
  done
else
  echo -e "\nYour keywords do not match!"
fi
echo
stty echo
This script asks the user for a password and the confirmation, and then refuse to terminate until user enters the password again. Some short explanations:

break and continue

Inside any loop we can use the break and continue statements. The break statement terminates execution of the loop bringing the control after the done statement. The continue statement transfers the control to the done statement, which continues execution of the loop.


References: