Conditions in the shell

Armed with expressions, let's look at our first decision-making process.

Remember in our eatout.sh script, we wanted to test whether the user has started eatout.sh with the correct number of parameters. So let's start by adding that functionality to that script.

Using the "if" statement

How do we add if statements? An if statement has the following form:

if condition
then
	do some operations
fi
                

Now, since I'm lecturing you, I might as well lecture you in good structured programming style.

When you start an 'IF' statement, put the 'THEN' statement on the next line, and make sure that you indent all the commands that you want within the 'THEN', by at least one tab or a couple of spaces. Finally, end your "IF" statement in a nice block format using the 'FI'. It's going to make maintaining your scripts much easier.

In our eatout.sh

if [ "$#" -lt 1 ]
then 
	echo "Usage: $0 <parameter>
	echo "where parameter is: italian|thai|smart|steakhouse"
	exit 1
fi
                

If we add this to the top of eatout.sh, our script will stop running if the user does not provide at least one positional parameter, or argument. Furthermore it will echo the usage command to explain how to use the script correctly and to avoid the error message.

The "if" "then" "else" statement

Equally, 'IF' has an associated construct, the 'ELSE':

if condition
then
	... <condition was TRUE, do these actions> ...
else
	... <condition was FALSE, do these actions> ...
fi
                

If a user runs the eatout.sh script with a correct parameter, then you can show them your favourite eating places, and if they don't it will exit with a status of 1 as well as a usage summary.

Notice that the condition that I used is a very simple one: I'm checking whether the number of parameters is less than one.

I'll leave it as an exercise for the user to check that the parameter that the user has entered is one of the allowed words (italian/steakhouse/smart).

To give you a hint, you could use:

[ $# -lt 1 -a "$1" = 'italian' or "$1" = 'steakhouse' or ..."
                

So you're to check that the number of parameters is at least one AND the $1 is equal to one of the allowed words. Is there a better way of doing this? There sure is.

What we might want to do is, if the restaurant we choose is a steakhouse, we might want to allow the user to choose between 5 different ways of doing their steak. For that we're going to want to do more than one test:

if $1 steakhouse
then 
	... ask how they like their steak done ...
else 
	if $1 smart
	then
		...
	else
		if $1 thai
		then
			...
		fi
	fi
fi
                

Note that there have to be matching fi's for every if statement.

The "elif" statement

As you can see reading this becomes quite difficult due to all the embedded if statements. There is an alternative construct called an elif which replaces the else-if with an elif and this makes the readability easier.

Look below for the syntax:

if $1 steakhouse
then 
	...
elif $1 smart
  then 
	...
  elif $1 thai
    then 
	...
    elif $1 italian 
      then 
	   ...
      else
	..
fi
                

Note that the final else is tied to the closest if. So in our example, the else statement will only be executed if $1 is NOT an italian resturant.

Is the 'IF' statement the best way of doing things? If you're going to do else if, else if, else if, etc. - then the answer is NO! It's bad programming practice to do this else-if, else-if nonsense. So how do we do things?

The "case" statement

Well we've got a 'CASE' statement. The structure of a 'CASE' statement is as follows:

case $1 in 
    pattern) ...
    	 ...
    	 ;;
    pattern) ...
    	 ...
    	 ;;
    *) ...
       ...
       ;;
esac
                

This means that we will match $1 to a pattern. The pattern will allow us to execute a series of statements and to finish this pattern we use a double semi-colon. We can then match the next pattern and, if it matches we do another whole series of things, ending with another double semi-colon.

If $1 matches none of the patterns, then it will be caught by the asterisk pattern since an a asterisk matches everything as we've seen in our regular expression and pattern theory.

The case statement makes your code a lot more legible, easier to maintain and allows you to match patterns.

Look at another example:

case $1 in 
	[Tt][Hh][Aa][Ii]) ...
		 ...
		 ;;
	Steakhouse) ...
		 ...
		 ;;
	*) echo "Sorry this pattern does not match any restaurant"
	   ...
	   ;;
esac
                

In this CASE statement, the first pattern matches ThAI or thAI or Thai, etc.

There's a better way of making your patterns case-insensitive. You could put the following line at the top of your script which would translate every character in your parameter $1 to uppercase:

RESTURANT_TYPE=(echo $1 |tr '[a-z]' '[A-Z]')
                

This will remove the long complicated pattern:

[Tt][Hh][Aa][Ii]) 
                

and we could instead just look for the pattern:

THAI
                

Similarly, if the user of our eatout.sh script only wants to type out part of the keyword for example, using:

./eatout.sh steak
                

instead of

./eatout.sh steakhouse
                

or

./eatout.sh meat 
                

instead of

./eatout.sh steakhouse 
                

These choices can be matched with the following pattern

steak|steakhouse|meat
                

Similarly this pattern

pasta|pizza|italian 
                

would match all of the following uses of our script:

eatout.sh pasta
                

and

eatout.sh pizza
                

and

eatout.sh italian
                

So you can match ranges of alternatives separating each with a vertical bar - the pipe character. So the case statement is most certainly the far better way to match alternatives with a script.

Exercises

  1. Write a script to test whether the free disk space on your largest partition is less than 10%. If it is, print a message to the screen indicating this fact.

  2. Modify your menu.sh written earlier in the course to allow the user to run the menu system with a parameter on the command line, producing output informing the user what option was selected on the command line. Do not use the CASE statement for this example.

  3. Rewrite the exercise in 2 above, this time using the CASE statement. Ensure that the user can use a combination of upper and lowercase charaters in their selection.

Challenge sequence:

Using the uptime from /proc/uptime, write a script which will determine how long your Linux machine has been 'UP', printing the following output to the console accoring to the results:

0 - 1 hour	"Obviously you're new to Linux. What's \
                    all this rebooting your machine nonsense"
1 - 5 hours	"Still a novice I see, but perhaps I could be wrong"
1 - 5 days	"Mmmm. You're getting better at this Linux thing!"