Main index

Introducing UNIX and Linux


Introduction to shells

Overview
Why do we need a shell?
Shell syntax
      Types of shell command
      Simple commands
      Pipelines
      Grouping commands
      Exit status
      List commands
Arithmetic
      Operators and functions
Making decisions
      The 'test' statement
            Operators used by 'test'
      The 'if' statement
Loops
      'For' loops
      'While' and 'until' loops
Searching for files
      Arguments to 'find'
Formatted output
      Arguments to 'printf'
Passing information to scripts
      Scripts with arguments
      Parameter expansion
Summary
Exercises

The 'test' statement

The command to accomplish this is test. Followed by arguments, test will give an exit status of 0 if the arguments evaluate to True. There are two ways of invoking test:

test arguments

[ arguments ]

and we shall use the latter for the rest of this book. To give you the flavour, the following checks whether file testfile exists and displays a suitable message if it does:

[ -e testfile ] && echo Testfile exists

Using the alternative syntax this would look like:

test -e testfile && echo Testfile exists

The operator -e, when presented as an argument to test, examines the following argument and, if that file exists, the command succeeds (exit status 0), otherwise it fails with exit status 1. Various options are available to test.

Worked example

Write a script which will read in the name of a file and print out a message indicating whether or not it is a directory.
Solution: Use test with option -d to check the file, and || and && to control which message is output.

echo Input a file name:   # Prompt the user ...
read FILENAME             # input a file name ...
([ -d $FILENAME ] &&      # check it's a directory ...
                          # then confirm this if so
       echo $FILENAME is a directory) ||
   echo $FILENAME is not a directory

A regular file is essentially one that is not a directory; there are other sorts of non-regular files, such as FIFO files, but they do not concern us here.

For instance, to check whether variable NAME has been set a value that is not the null string, we might have:

[ "$NAME" ] || echo NAME is unset

Note that we have enclosed $NAME in double quotes; test expects to get an argument, and if we did not enclose it in quotes, and NAME was unset (or contained only whitespace), the line would become

[ ] || echo NAME is unset

prior to execution, which would give an error, whereas

[ "" ] || echo NAME is unset

would be OK.

Worked example

Write a script to greet the person running it if they are logged on as user chris.
Solution: Use logname to check the user's name (not the variable LOGNAME, which might accidentally have been changed), and test to compare it with chris.

[ "$( logname )" = chris ] && echo Hello Chris

The numerical checks listed that test can perform are the principal way of doing numerical comparisons using the shell. They only work with whole numbers, however, and if you wish to perform complex tasks using floating-point numbers you are advised to use bc. As an example, we code the solution to the question posed at the start of this section, which was: 'If file A is smaller than 100 lines then display it on the terminal, otherwise tell me that it's bigger than 100 lines.'

FILESIZE=$( wc -l A )    # Use wc -l to count the lines
([ "$FILESIZE" -gt 100 ] && echo File too big) || cat A

There is a difference between the operators = and -eq For instance,

[ 0 -eq 00 ]

succeeds, as 0 and 00 are numerically equal, but

[ 0 = 00 ]

fails, as they are different strings of characters.

Worked example

Write a script to request you to type in a number, and then to guess its square; it should then either congratulate you or tell you the correct answer.
Solution: After reading in a number into variable NUMBER, construct an expression $NUMBER * $NUMBER to be piped to bc, assigning the output of the calculation to SQUARE. Then, after reading in the user's guess into variable GUESS, use test to check whether GUESS and SQUARE are the same.

Notice the different 'style' of comments used here - each on a line of its own; this style is preferred with long command lines.

# Prompt the user and read in the number
echo Type in a number:
read NUMBER

# Evaluate the square of the number using bc
SQUARE=$( echo "$NUMBER * $NUMBER" | bc )

# Prompt the user and read in the guessed answer
echo Guess its square:
read GUESS

# If the guess is equal to the square, confirm ...
([ "$GUESS" -eq "$SQUARE" ] && echo Correct) ||
# otherwise display the correct answer
    echo The correct answer is $SQUARE

Since test requires arguments, you must separate those arguments from the word test by whitespace. Similarly, if you are using the square bracket notation for test, you should separate the square brackets from what is inside the brackets. Otherwise, the brackets themselves would become part of the strings which they should enclose. For instance,

[ hello = hello]

would attempt to compare string hello with string hello] and think that you had forgotten to provide the closing square bracket.


Copyright © 2002 Mike Joy, Stephen Jarvis and Michael Luck