Testing in bash

  1. Unix Shell Scripting
  2. Shell Basics
  3. Testing in bash
  4. Capturing User Input
  5. Scripting : Exercise 1
  6. Debugging
  7. Bourne/Bash/Korn Commands
  8. Shell Variables
  9. IO Redirection
  10. Pipes
  11. Operators, Wildcards and Expressions
  12. Flow Control
  13. Scripting : Exercise 2
  14. Shell Differences
  15. String Functions
  16. awk
  17. xargs
  18. Power Tools
  19. Exercise 3

The test Command

The command used in most conditionals is the test command. test returns true or false (more accurately, exits with 0 or non-zero status) depending on whether the test is passed or failed. It works like this:

test operand1 operator operand2

Which ends up looking like the bold-faced part of this line:

if [ $x -lt $y ]
# is $x less than $y ?

There are several things to know about test.

  • While there is in fact a test command (see man test), it is typically abbreviated as “ [ ” and the character [ is actually aliased to test:
    [ operand1 operator operand2 ]
  • The ] character, on the other hand, gets no special meaning and is just there to protect programmer sanity.
  • There must – MUST – be spaces inside the square brackets! Look at the example just above. Failing to include these spaces WILL break your script.

For some tests, there need be only one operand (operand2). This looks like:

if [ -n "$empty_string" ]
# is empty_string really an empty string?

Consider a few examples:

#!/bin/bash
x=3
y=4
empty_string=""
# a simple test of inequality: is $x less than $y?
if [ $x -lt $y ]; then
echo "\$x=${x}, which is less than \$y=${y}."
# Notice how we use the escape character 
# so we can print a literal dollar sign.
# The curly braces let us print a variable
# right next to another character, and act as
# a form of quotation mark.
fi
# a test for non-empty:
if [ -n "$non-empty_string" ]; then
echo "non empty string is non_empty"
fi
# test to see if ~/.bashrc exists
if [ -e "${HOME}/.bashrc" ]; then
echo "you have a .bashrc file"
fi
# is it a symlink:
if [ -L "${HOME}/.bashrc" ]; then
echo "it is a symbolic link"
# is it a regular file: -f
elif [ -f "${HOME}/.bashrc" ]; then
echo "it is a regular file"
# don't forget an escape hatch: else
else
echo "you have no .bashrc file"
fi

Echoing back values in test statements is one of the most powerful debugging techniques you can use.
Simply comment out any unneeded echoes when you place the script into production use.

Pitfalls

The test command needs to be in the form

[<space>operand1<space>operator<space>operand2<space>]
or
[<space>operator<space>operand2<space>]

you must have these spaces, since the shell considers the first block containing no spaces to be either an operator (if it begins with a ‘-‘) or an operand (if it doesn’t).

This will trip you up. It’s a different syntax than the syntax you use everywhere else. Normally, when you’re assigning a value to a variable, you use a command like:

VAR=1

In this instance, though, you’re using that “operand1<space>operator<space>operand2″ syntax, which means you MUST have spaces before and after the equal sign! (Remember, this only applies to test situations.)

So for example, this:

if [ 1-eq2 ]; then # WRONG!
echo "hello"
fi

gives exactly the “wrong” output (i.e., it echos “hello” since it sees an operand but no operator.)

Another potential trap comes from not protecting variables in quotes. There are many good reasons for using quotes all the time, or almost all of the time. Failing to do this when you have variables expanded inside tests can result in very weird bugs. Here’s an example:

#!/bin/bash
x="-n"
y=""
if [ $x = $y ] ; then
echo "x=y"
else
echo "Unequal!"
fi

This will give misleading output since the shell expands our expression to

if [ -n = ]

and the string “=” has non zero length. To avoid this error, you must use quotes:

if [ "$x" = "$y" ]

Now your shell will see:

if [ -n = "" ]

Test Operators

This is not an exhaustive list, but does contain the most often-used operators. See man test for all options.

Operator produces true if… number of operands
-n
operand is non-zero length 1
-z
operand is zero length 1
-d
a directory exists named operand 1
-f
a file exists named operand 1
-eq
the operands are integers and they are equal 2
-neq
the opposite of -eq 2
=
the operands are equal (as strings) 2
!=
not equal to 2
-lt
operand1 is strictly less than operand2 (both operands should be integers) 2
-gt
operand1 is strictly greater than operand2 (both operands should be integers) 2
-ge
operand1 is greater than or equal to operand2 (both operands should be integers) 2
-le
operand1 is less than or equal to operand2 (both operands should be integers) 2

Assignment

Create if statements in your user logging script that check for the presence of your logfile and create it only if necessary.

Add echo statements to inform you of execution details at each step.