Table of Contents
We need to be able make decisions on information that we have. Generally this takes the following form:
"if a particular condition occurs, then we can do this otherwise (else) we can do that." |
So we're going to be talking about the if-then condition. Before we do that, we need to understand the command 'test'.
The test command is really what all decision conditions are based on.
You can do an:
info test |
to see what information the test gives you.
Let's take a timeout from looking at the test command, to understand what "true" really is, in terms of the shell.
In terms of any programming language one has the boolean operators, true and false.
Depending on the language we are programming in, true and false can have different values. In the shell, being "true" is represented by a 0 (zero) and anything else is false. So the values 1, 25 and 2000 represent the state of being "false".
In a previous chapter we typed:
ping -c1 199.199.199.1 |
which returned a non-null exit value that was displayed with the command
echo $? |
Let's test a couple of things using the following commands:
who; echo $? |
produces a 0.
Try:
who |grep root; echo $? |
Now, try the above command again, only this time:
who |grep root; test $? |
will test whether the exit value of the grep was 0 (in other words, did it exit correctly? i.e. a 0 exit status would indicate that the user 'root' was logged in) or was the exit value anything other than 0 (did it execute incorrectly, i.e. was the user 'root' not there?).
The different types of tests that we can do are:
a string test
a numeric test
a file test
There are other tests we will discuss later, but let's start with the string test Execute the following on the command line:
NAME="hamish" test $NAME = hamish echo $? |
Now you'll notice a couple of things: the second line has white-space between the variable name, the equals sign and the variable value, since test takes three parameters (the variable name, the test condition and the value that we're testing the variable against).
What output does the above example produce? If NAME was hamish, test returns a 0. If NAME was not hamish, this would've returned a 1.
So I'm going to run that again but I'm going to test it against the value of 'joe' with:
test $NAME = joe echo $? |
Since 'joe' is not equal to 'hamish', the above example produces a value of 1. String tests can become tricky. Lets create a variable called 'BLANKS' containing 5 spaces:
BLANKS=" " |
Now
test $blanks echo $? |
What does this produce? A false value of 1, but clearly I shouldn't have got a false value because a false value would've indicated that the variable was not set, and in fact, the variable was set, it has a non-null value.
This time let's try:
test "$blanks" echo $? |
and you should see that the value should be true (0). This is a very important thing to bear in mind when doing tests, a good safety net as it were to always enclose your variable in quotes not ticks! If I enclosed the variable in ticks as follows:
test '$blanks' echo $? |
this would always produce true. Why is that? Because it's testing whether this is a string.
Since ticks ignore the $, it is always considered to be a string - $blanks. Thus, testing a string will always produce true.
If I enclose the variable in double quotes, test interprets the $blanks to be five spaces, and subsequently tests the five spaces returning a true value.
The test '$blanks' produces TRUE and the test "$blanks" produces TRUE, but for VERY different reasons.
As a precaution then: when you do tests, enclose your variables in double quotes. Saying:
test "$NAME" = "hamish" |
or
test "$NAME" = hamish |
will give you the desired output time and again.
Something that's often done in scripts, specifically in configure scripts, is to test whether a variable has been or has not been set. It's often achieved using the following construct:
test "${NAME}x" = x |
If the variable NAME is not set then the left hand side of the equation will only be equal to an 'x' which is equal to the right hand side and thus the answer of an unset variable would be TRUE.
However if the NAME is set, then I would end up with 'hamishx = x'. Clearly this is going to be FALSE. This is an effective way of testing whether a variable is set or not set.
If you take the time to look at the "configure" scripts for many Open Source packages, (configure scripts are the scripts used to configure the software prior to compilation) they are mostly shell scripts, which run a host of tests, testing whether variables in the configuration files have been set.
There are also a couple of string tests that have special meaning:
string test | meaning |
-z | zero-length |
-n | non-zero length |
so if we said:
blanks=" " test -z "$blanks" echo $? |
We should expect a FALSE (1), since $blanks is not of 0 length. Conversely, if we did a:
test -n "$blanks" echo $? |
It would produce a TRUE (0) since $blanks is a non-zero length string.
Those are string type tests, what about numeric tests? Well unfortunately the test comparisons vary for the different types of tests:
String Test | Numeric Test |
= | -eq |
!= | -neq |
<= | -le |
>= | -ge |
> | -gt |
< | -lt |
String tests operate on the ascii values of the string. What about numeric tests?
Set the variable 'x' to have a value of 101.
x=101 |
How would we test the following expression?
$x < 10 |
We could write this as a numeric test in the following manner:
test "$x" -lt 10 echo $? |
You're going to be returned with a value 1 (FALSE) since 101 is NOT less than 10. Notice the test comparison is a '-lt' and not a '<'.
Similarly for:
test "$x" -lt 102 echo $? |
This will return a value 0 (TRUE) since 101 < 102.
To find out more on the other numeric test operators:
info test |
The third type of tests that we want to talk about are tests on files.
For instance: "is this file a regular file?", "is it a directory?", "is a symbolic link?", "is it a hardlink?" So you could say:
test -f file |
To test whether a file is a normal file. Or if you want to test whether a file is a directory, for example (notice the period ( . ) after the -d test:)
test -d . |
It should return a value of 0 because '.' represents your current directory which is obviously a directory.
If you say:
test -d .bashrc |
It should return a 1 (FALSE), because .bashrc is not a directory, it's a file. You might want to test if something is a symbolic link using '-L' et cetera.
Try the exercises to familiarise yourself with other test types.
Set the variables as follows:
NAME="<insert your first name here>"
SURNAME="<insert your surname here>"
AGE=<insert your age>
MARRIED="<insert a 'Y' or a 'N' here>"
Now perform the following tests, indicating (before performing the test) whether the outcome will be TRUE (0) or FALSE(1), or unknown.
test "$NAME" = "joe"
test "$AGE" > "35"
test "SURNAME" -lt "Bloggs"
test '$AGE' -lt 35
test "NAME" = <insert your first name here>
test "35" -gt "$AGE"
Using the files in your directory, perform the following tests, again indicating what the outcome will be:
test -f .
test -e ..
touch somefile; test -s somefile
ln somefile hardlink; test somefile -ef hardlink
test -c /dev/hda