CONDITIONS & INTERACTIVE SCRIPTS

4.1. IF clause

General
At times you need to specify different courses of action to be taken in a shell script, depending on the success or failure of a command. The if construction allows you to specify such conditions.

The most compact syntax of the if command is:
        if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi
or
         if [[ some condition ]]; then do_something fi

Double brackets are used to enclose the conditional expression

The TEST-COMMAND list is executed, and if its return status is zero, the CONSEQUENT-COMMANDS list is executed. The return status is the exit status of the last command executed, or zero if no condition tested true.

The TEST-COMMAND often involves numerical or string comparison tests, but it can also be any command that returns a status of zero when it succeeds and some other status when it fails. Unary expressions are often used to examine the status of a file. If the FILE argument to one of the primaries is of the form /dev/fd/N, then file descriptor “N” is checked. stdin, stdout and stderr and their respective file descriptors may also be used for tests.

Expressions used with if
The table below contains an overview of the so-called “primaries” that make up the TEST-COMMAND command or list of commands. These primaries are put between square brackets to indicate the test of a conditional expression.

Primary expressions

Primary                                                       Meaning
[ -a FILE ]                                                      True if FILE exists.
[ -b FILE ]                                                      True if FILE exists and is a block-special file.
[ -c FILE ]                                                      True if FILE exists and is a character-special file.
[ -d FILE ]                                                      True if FILE exists and is a directory.
[ -e FILE ]                                                      True if FILE exists.
[ -f FILE ]                                                       True if FILE exists and is a regular file.
[ -g FILE ]                                                      True if FILE exists and its SGID bit is set.
[ -h FILE ]                                                      True if FILE exists and is a symbolic link.
[ -k FILE ]                                                      True if FILE exists and its sticky bit is set.
[ -p FILE ]                                                      True if FILE exists and is a named pipe (FIFO).
[ -r FILE ]                                                       True if FILE exists and is readable.
[ -s FILE ]                                                       True if FILE exists and has a size greater than zero.
[ -t FD ]                                                          True if file descriptor FD is open and refers to a terminal.
[ -u FILE ]                                                       True if FILE exists and its SUID (set user ID) bit is set.
[ -w FILE ]                                                      True if FILE exists and is writable.
[ -x FILE ]                                                       True if FILE exists and is executable.
[ -O FILE ]                                                      True if FILE exists and is owned by the effective user ID.
[ -G FILE ]                                                      True if FILE exists and is owned by the effective group ID.
[ -L FILE ]                                                       True if FILE exists and is a symbolic link.
[ -N FILE ]                                                      True if FILE exists and has been modified since it was last read.
[ -S FILE ]                                                       True if FILE exists and is a socket.
[ FILE1 -nt FILE2 ]                                         True if FILE1 has been changed more recently than FILE2, or if FILE1 exists and                                                                                FILE2 does not.
[ FILE1 -ot FILE2 ]                                         True if FILE1 is older than FILE2, or if FILE2 exists and FILE1 does not.

[ FILE1 -ef FILE2 ]                                         True if FILE1 and FILE2 refer to the same device and inode numbers.
[ -o OPTIONNAME ]                                     True if shell option “OPTIONNAME” is enabled.
[ -z STRING ]                                                  True if the length of “STRING” is zero.
[ -n STRING ] or [ STRING ]                          True if the length of “STRING” is non-zero.
[ STRING1 == STRING2 ]                              True if the strings are equal. “=” may be used instead of “==” for strict POSIX                                                                                     compliance.
[ STRING1 != STRING2 ]                               True if the strings are not equal.
[ STRING1 < STRING2 ]                                True if “STRING1” sorts before “STRING2” lexicographically in the current locale.
[ STRING1 > STRING2 ]                                True if “STRING1” sorts after “STRING2” lexicographically in the current locale.
[ ARG1 OP ARG2 ]                                         “OP” is one of -eq, -ne, -lt, -le, -gt or -ge. These arithmetic binary operators return                                                                           true if “ARG1” is equal to, not equal to, less than, less than or equal to, greater                                                                               than, or greater than or equal to “ARG2”, respectively. “ARG1” and “ARG2” are                                                                                 integers.

Combining expressions

Operation                                                    Effect
[ ! EXPR ]                                                        True if EXPR is false.
[ ( EXPR ) ]                                                      Returns the value of EXPR. This may be used to override the normal precedence                                                                            of operators.
[ EXPR1 -a EXPR2 ]                                        True if both EXPR1 and EXPR2 are true.
[ EXPR1 -o EXPR2 ]                                        True if either EXPR1 or EXPR2 is true.

The [ (or test) built-in evaluates conditional expressions using a set of rules based on the number of arguments. More information about this subject can be found in the Bash documentation. Just like the if is closed with fi, the opening square bracket should be closed after the conditions have been listed.

Commands following the then statement
The CONSEQUENT-COMMANDS list that follows the then statement can be any valid UNIX command, any executable program, any executable shell script or any shell statement, with the exception of the closing fi. It is important to remember that the then and fi are considered to be separated statements in the shell. Therefore, when issued on the command line, they are separated by a semi-colon.

In a script, the different parts of the if statement are usually well-separated.

Activity:
o If clause to check file is existing.
       vi checkfile.sh
       #!/bin/bash
       echo “This scripts checks the existence of the messages file.”
       echo “Checking…”
        if [ -f /var/log/messages ]
           then
               echo “/var/log/messages exists.”
            else
                echo “/var/log/messages DO NOT exists.”
        fi
        echo
        echo “…done. “
        chmod +x checkfile.sh
        ./checkfile.sh
o Check if the file is protected against accidental overwriting using redirection.
        if [ -o noclobber ]
           then
              echo “Your files are protected against accidental overwriting using redirection.”
        fi
o Testing exit status with if.
         if [ $? -eq 0 ]
           then
                echo “Earlier command executed successfully”
            else
                echo “Earlier command DID NOT execute successfully”
          fi
o Testing with a command and then $?
           grep oracle /etc/passwd
           if [ $? -eq 0 ]
                then
                    echo “Earlier command executed successfully”
                else
                    echo “Earlier command DID NOT execute successfully”
            fi

            grep oraC /etc/passwd
               if [ $? -eq 0 ]
                   then
                        echo “Earlier command executed successfully”
                    else
                         echo “Earlier command DID NOT execute successfully”
                 fi
o Testing this user existence within if clause.
                 if ! grep oracle /etc/passwd
                     then
                           echo “User doesn’t exists”
                      else
                            echo “User exists”
                   fi

 o Working with numeric.
                   num=`cat revenues| wc -l `
                       if [ “$num” -gt “4” ]
                           then
                               echo
                               echo “Condition is true”
                            else
                                echo “Condition is false”
                        fi
o Working with string comparisons.
               if [ “$(whoami)” != ‘root’ ]
                  then
                     echo “You have no permission to run $0 as non-root user.”
               fi

 

o Double-pipes for a logical OR statement
              if [[ $a -lt 25 || $a –gt 50 ]]
                  then
                       echo ” The value $a either: less than 25 or greater than 50 “
                  else
                       echo ” The value of $a is between 25 and 50 “
              fi

 

 

4.2. Advanced IF usage

Checking command line arguments

Instead of setting a variable and then executing a script, it is frequently more elegant to put the values for the variables on the command line.

We use the positional parameters $1, $2, …, $N for this purpose. $# refers to the number of command line arguments. $0 refers to the name of the script.

Activity:
o Example with two arguments we passed.
          vi weight.sh
          #!/bin/bash
          # This script prints a message about your weight if you give it your
          # weight in kilos and height in centimeters.
          weight=”$1″
          height=”$2″
          idealweight=$[$height – 100]
          if [ $weight -le $idealweight ] ;

              then

                    echo “You should eat a bit more fat.”

              else
                    echo “You should eat a bit more fruit.”
           fi
o Execute this script with debug mode to verify the process flow.
           bash -x weight.sh 60 175
o Let us edit this script to also include verifying number of arguments passed.
           vi weight.sh
           #!/bin/bash
           # This script prints a message about your weight if you give it your
           # weight in kilos and height in centimeters.
           if [ ! $# == 2 ]; then
                 echo “Usage: $0 weight_in_kilos length_in_centimeters”
                 exit
           fi
           weight=”$1″
           height=”$2″
           idealweight=$[$height – 100]
           if [ $weight -le $idealweight ] ; then
                echo “You should eat a bit more fat.”
            else
                echo “You should eat a bit more fruit.”
          fi

 

if/then/elif/else constructs

General

This is the full form of the if statement:

if TEST-COMMANDS; then
CONSEQUENT-COMMANDS;
elif MORE-TEST-COMMANDS; then
MORE-CONSEQUENT-COMMANDS;
else ALTERNATE-CONSEQUENT-COMMANDS;
fi

The TEST-COMMANDS list is executed, and if its return status is zero, the CONSEQUENT-COMMANDS list is executed. If TEST-COMMANDS returns a non-zero status, each elif list is executed in turn, and if its exit status is zero, the corresponding MORE-CONSEQUENT-COMMANDS is executed and the command completes. If else is followed by an ALTERNATE-CONSEQUENT-COMMANDS list, and the final command in the final if or elif clause has a non-zero exit status, then ALTERNATE-CONSEQUENT-COMMANDS is executed. The return status is the exit status of the last command executed, or zero if no condition tested true.

Activity:
o Example with complex variable and simple if.
       vi disktest.sh
       #!/bin/bash
       # This script does a very simple test for checking disk space.
       space=`df -h | awk ‘{print $5}’ | grep % | grep -v Use | sort -n | tail -1 | cut -d “%” -f1 -`
       alertvalue=”85″
       if [ “$space” -ge “$alertvalue” ]; then
             echo “ALERT!!!! One of my disks is nearly full!”
         else
             echo “Disk space is normal”
       fi
o Example with nested if clauses.
          vi testleap.sh
          #!/bin/bash
          # This script will test if we’re in a leap year or not. year=`date +%Y`
          if [ $[$year % 400] -eq “0” ]; then
                  echo “This is a leap year. February has 29 days.”
          elif [ $[$year % 4] -eq 0 ]; then
                 if [ $[$year % 100] -ne 0 ]; then
                      echo “This is a leap year, February has 29 days.”
                   else
                      echo “This is not a leap year. February has 28 days.”
                 fi
             else
                echo “This is not a leap year. February has 28 days.”
           fi
o Using exit numbers in the if clauses.
           #!/bin/bash
           #This script lets you present different menus to Phinx. He will only be happy
           # when given a fish. We’ve also added a dolphin and (presumably) a camel.
           menu=”$1″
           animal=”$2″
           if [ “$menu” == “fish” ]; then
              if [ “$animal” == “penguin” ]; then
                echo “Hmmmmmm fish… Phinx happy!”
           elif [ “$animal” == “dolphin” ]; then
                echo “Its okay for Phinx”
            else
                echo ” Yuck Phinx dont like “
               fi
            else
                if [ “$animal” == “penguin” ]; then

                     echo ” Phinx don’t like that. Phinx wants fish!”
                     exit 1
                elif [ “$animal” == “dolphin” ]; then
                     echo “XXXXXXXXXXXXXXXXXX!”
                     exit 2
                   else echo “Will you read this sign?!”
                    exit 3
               fi
          fi

 

4.3. CASE statements

Simplified conditions

Nested if statements might be nice, but as soon as you are confronted with a couple of different possible actions to take, they tend to confuse. For the more complex conditionals, use the case syntax:
case EXPRESSION in CASE1) COMMAND-LIST;; CASE2) COMMAND-LIST;; … CASEN) COMMAND-LIST;; esac

Each case is an expression matching a pattern. The commands in the COMMAND-LIST for the first match are executed.

The “|” symbol is used for separating multiple patterns, and the “)” operator terminates a pattern list. Each case plus its according commands are called a clause. Each clause must be terminated with “;;”. Each case statement is ended with the esac statement.

Activity:
o In the example, we demonstrate use of cases for sending a more selective warning message with the disktest.sh script:
        #!/bin/bash
        # This script does a very simple test for checking disk space.
        space=`df -h | awk ‘{print $5}’ | grep % | grep -v Use | sort -n | tail -1 | cut -d “%” -f1 -`
        case $space in
           [1-6]*)
                Message=”Disk space is well below threshold.”
                ;;
           [7-8]*)
                Message=”Please do some housekeeping. There’s a partition that is $space % full.”
                ;;
          9[1-8])
                Message=”Order for a new disk… One partition is $space % full.”
                ;;
          99)
               Message=”ALERTTTTTT! There’s a partition at $space %!”
               ;;
          *)
              Message=”Please initiate a bridge, as this will be a P1 incident…”
                ;;
         esac

         echo $Message
o Case on string value comparisions.
           case “$1” in
               start)
                          start
                          ;;
               stop)
                          stop
                          ;;
               status)
                         status filename
                         ;;
               restart)
                          stop
                          start
                          ;;
               Condition restart)
                          if test “x`pidof filename`” != x; then
                           stop
                           start 
                         fi
                         ;;
                *)
                        echo $”Usage: $0 {start|stop|restart|conditionrestart|status}”
                        exit 1
            esac

 

4.4. Display user messages

Interactive or not?

Some scripts run without any interaction from the user at all. Advantages of non-interactive scripts include:
o The script runs in a predictable way every time.
o The script can run in the background.

Many scripts, however, require input from the user, or give output to the user as the script is running. The advantages of interactive scripts are, among others:
o More flexible scripts can be built.
o Users can customize the script as it runs or make it behave in different ways.
o The script can report its progress as it runs.

When writing interactive scripts, never hold back on comments. A script that prints appropriate messages is much more user-friendly and can be more easily debugged. A script might do a perfect job, but you will get a whole lot of support calls if it does not inform the user about what it is doing. So include messages that tell the user to wait for output because a calculation is being done. If possible, try to give an indication of how long the user will have to wait. If the waiting should regularly take a long time when executing a certain task, you might want to consider integrating some processing indication in the output of your script.

When prompting the user for input, it is also better to give too much than too little information about the kind of data to be entered. This applies to the checking of arguments and the accompanying usage message as well.

Bash has the echo and printf commands to provide comments for users, and although you should be familiar with at least the use of echo by now, we will discuss some more examples in the next sections.

Using the echo built-in command

The echo built-in command outputs its arguments, separated by spaces and terminated with a newline character. The return status is always zero. echo takes a couple of options:
-e: interprets backslash-escaped characters.
-n: suppresses the trailing newline.

Activity:
o Let us write two scripts in a better format than earlier with user messages using echo.
      #!/bin/bash
      # This script lets you present different menus to Phinx. He will only be happy
      # when given a fish. To make it more fun, we added a couple more animals.
      if [ “$menu” == “fish” ]; then
            if [ “$animal” == “penguin” ]; then
                echo -e ” Hmmmmmm fish… Phinx happy!\\n”
           elif [ “$animal” == “dolphin” ]; then
                echo -e ” Its okay for Phinx \\n” else
                echo -e ” Yuck Phinx dont like \\n” fi
           else
               if [ “$animal” == “penguin” ]; then
                  echo -e ” Phinx don’t like that. Phinx wants fish!\\n”
                  exit 1
              elif [ “$animal” == “dolphin” ]; then
                  echo -e ” XXXXXXXXXXXXXXXXXX! “
                  exit 2
              else
                   echo -e “Will you read this sign?! Don’t feed the “$animal”s!\\n”
                   exit 3
               fi
       fi
o Example which includes case conditional statements.
           #!/bin/bash
           # This script acts upon the exit status given by penguin.sh
           if [ “$#” != “2” ]; then
                   echo -e “Usage of the feed script:\\t$0 food-on-menu animal-name\\n”
                   exit 1
            else

                  export menu=”$1″
                  export animal=”$2″
                  echo -e “Feeding $menu to $animal…\\n” feed=”/home/oracle/scripting/penguin.sh”
                  $feed
                  result=”$?”
                  echo -e “Done feeding.\\n”
              case “$result” in
                   1)
                       echo -e “Guard: \\”You’d better give’m a fish, else he gets violent…\\”\\n”
                        ;;
                  2)

                       echo -e “Guard: \\”No wonder they will be extinct…\\”\\n”
                        ;;
                  3)
                       echo -e “Guard: \\”Buy packaged foods ***\\”\\n”
                       echo -e “Guard: \\”Don’t provide unhealthy foods\\”\\n”
                       ;;
                  *)
                       echo -e “Guard: \\”Don’t forget the guide!\\”\\n”
                        ;;
               esac
        fi
        echo “Leaving…”
        echo -e “\\a\\a\\aThanks for visiting, hope to see you again soon!\\n”
o Run feed.sh to re-run penguin.sh script.
             ./feed.sh apple camel 

 

Escape sequences used by the echo command
Sequence                                    Meaning
\\a                                                  Alert (bell).
\\b                                                 Backspace.
\\c                                                  Suppress trailing newline.
\\e                                                  Escape.
\\f                                                  Form feed.
\\n                                                  Newline.
\\r                                                  Carriage return.
\\t                                                  Horizontal tab.
\\v                                                  Vertical tab.
\\\\                                                 Backslash.
\\0NNN                                         The eight-bit character whose value is the octal value NNN (zero to three octal digits).
\\NNN                                           The eight-bit character whose value is the octal value NNN (one to three octal digits).
\\xHH                                            The eight-bit character whose value is the hexadecimal value (one or two hexadecimal                                                                digits).

 

4.5. User input

Using the read built-in command
The read built-in command is the counterpart of the echo and printf commands. The syntax of the read command is as follows:
read [options] NAME1 NAME2 … NAMEN

One line is read from the standard input, or from the file descriptor supplied as an argument to the -u option. The first word of the line is assigned to the first name, NAME1, the second word to the second name, and so on, with leftover words and their intervening separators assigned to the last name, NAMEN. If there are fewer words read from the input stream than there are names, the remaining names are assigned empty values.

The characters in the value of the IFS variable are used to split the input line into words or tokens; see the section called “Word splitting”. The backslash character may be used to remove any special meaning for the next character read and for line continuation.

If no names are supplied, the line read is assigned to the variable REPLY.

The return code of the read command is zero, unless an end-of-file character is encountered, if read times out or if an invalid file descriptor is supplied as the argument to the -u option.

Options to the read built-in

Option                      Meaning
-a ANAME                  The words are assigned to sequential indexes of the array variable ANAME, starting at 0. All elements                                      are removed from ANAME before the assignment. Other NAME arguments are ignored.
-d DELIM                    The first character of DELIM is used to terminate the input line, rather than newline.
-e                                 readline is used to obtain the line.
-n NCHARS                 read returns after reading NCHARS characters rather than waiting for a complete line of input.
-p PROMPT                Display PROMPT, without a trailing newline, before attempting to read any input. The prompt is                                                displayed only if input is coming from a terminal.
-r                                  If this option is given, backslash does not act as an escape character. The backslash is considered to                                         be part of the line. In particular, a backslash-newline pair may not be used as a line continuation.
-s                                  Silent mode. If input is coming from a terminal, characters are not echoed.

-t TIMEOUT                Cause read to time out and return failure if a complete line of input is not read within TIMEOUT                                                 seconds. This option has no effect if read is not reading input from the terminal or from a pipe.
-u FD                           Read input from file descriptor FD.

 

Activity:
o Writing a better leaptest.sh script.
               #!/bin/bash
               # This script will test if you have given a leap year or not.
                year=”$1″
                if (( (“$year” % 400) == “0” )) || (( (“$year” % 4 == “0”) && (“$year” % 100 != “0”) )); then
                       echo “$year is a leap year.”
                else
                       echo “This is not a leap year.”
                 fi
o Writing a script to prompt user input.
                 vi relatives.sh
                 #!/bin/bash
                 # This is a program that keeps your address book up to date.
                  relatives=”/home/oracle/scripting/relatives”
                  echo “Hello, “$USER”. This script will register you in Dian’s relatives database.”
                  echo -n “Enter your name and press [ENTER]: “
                  read name
                  echo -n “Enter your gender [m or f] : “
                  read -n 1 gender
                  echo
                  grep -i “$name” “$relatives”
                  if [ $? == 0 ]; then
                          echo “You are already registered, quitting.”
                          exit 1
                  elif [ “$gender” == “m” ]; then

                          echo “$name $gender” >> “$relatives”
                          echo “You are added to Dian’s relatives list.”
                          exit 1
                  else
                          echo -n “How old are you?”
                read age
                        if [ $age -lt 25 ]; then
                              echo -n “Where do you reside? “
                              read place
                              echo “$name $gender $age $place” >> “$relatives”
                              echo “You are added to Dian’s relatives list. Thank you so much!”
                         else
                              echo “$name $gender $age” >> “$relatives”
                              echo “You are added to Dian’s relatives list.”
                              exit 1
                         fi
                fi

 

 To continue with Tutorial 5, please click here

 To start reading from Chapter 1, please click here