3.5. Tests

The if/then construct tests whether a condition is true, and if so, executes one or more commands. Note that in this context, 0 (zero) will evaluate as true, as will a random string of alphanumerics. Puzzling out the logic of this is left as an exercise for the reader.


Example 3-9. What is truth?

   1 #!/bin/bash
   2 
   3 if [ 0 ]
   4 #zero
   5 then
   6   echo "0 is true."
   7 else
   8   echo "0 is false."
   9 fi
  10 
  11 if [ ]
  12 #NULL (empty condition)
  13 then
  14   echo "NULL is true."
  15 else
  16   echo "NULL is false."
  17 fi
  18 
  19 if [ xyz ]
  20 #string
  21 then
  22   echo "Random string is true."
  23 else
  24   echo "Random string is false."
  25 fi
  26 
  27 if [ $xyz ]
  28 #string
  29 then
  30   echo "Undeclared variable is true."
  31 else
  32   echo "Undeclared variable is false."
  33 fi
  34 
  35 exit 0

Exercise. Explain the behavior of Example 3-9, above.

   1 if [ condition-true ]
   2 then
   3    command 1
   4    command 2
   5    ...
   6 else
   7    # Optional (may be left out if not needed).
   8    # Adds default code block executing if original condition tests false.
   9    command 3
  10    command 4
  11    ...
  12 fi

Add a semicolon when 'if' and 'then' are on same line.

   1 if [ -x filename ]; then

elif

This is a contraction for else if. The effect is to nest an inner if/then construction within an outer one.

   1 if [ condition ]
   2 then
   3    command
   4    command
   5    command
   6 elif
   7 # Same as else if
   8 then
   9    command
  10    command
  11 else
  12    default-command
  13 fi

The test condition-true construct is the exact equivalent of if [condition-true ]. The left bracket [ is, in fact, an alias for test. (The closing right bracket ] in a test should not therefore be strictly necessary, however newer versions of bash detect it as a syntax error and complain.)


Example 3-10. Equivalence of [ ] and test

   1 #!/bin/bash
   2 
   3 echo
   4 
   5 
   6 if test -z $1
   7 then
   8   echo "No command-line arguments."
   9 else
  10   echo "First command-line argument is $1."
  11 fi
  12 
  13 # Both code blocks are functionally identical.
  14 
  15 if [ -z $1 ]
  16 # if [ -z $1
  17 # also works, but outputs an error message.
  18 then
  19   echo "No command-line arguments."
  20 else
  21   echo "First command-line argument is $1."
  22 fi
  23 
  24 
  25 echo
  26 
  27 exit 0

3.5.1. File test operators

Returns true if...

-e

file exists

-f

file is a regular file

-s

file is not zero size

-d

file is a directory

-b

file is a block device (floppy, cdrom, etc.)

-c

file is a character device (keyboard, modem, sound card, etc.)

-p

file is a pipe

-L

file is a symbolic link

-S

file is a socket

-r

file is readable (has read permission)

-w

file has write permission

-x

file has execute permission

-g

group-id flag set on file

-u

user-id flag set on file

-k

"sticky bit" set (if user does not own a directory that has the sticky bit set, she cannot delete files in it, not even files she owns)

-O

you are owner of file

-G

group-id of file same as yours

-t n

file descriptor n is open

This usually refers to stdin, stdout, and stderr (file descriptors 0 - 2).

f1 -nt f2

file f1 is newer than f2

f1 -ot f2

file f1 is older than f2

f1 -ef f2

files f1 and f2 are links to the same file

!

"not" -- reverses the sense of the tests above (returns true if condition absent).


Example 3-11. Tests, command chaining, redirection

   1 #!/bin/bash
   2 
   3 # This line is a comment.
   4 
   5 filename=sys.log
   6 
   7 if [ ! -f $filename ]
   8 then
   9   touch $filename; echo "Creating file."
  10 else
  11   cat /dev/null > $filename; echo "Cleaning out file."
  12 fi  
  13 
  14 # Of course, /var/log/messages must have
  15 # world read permission (644) for this to work.
  16 tail /var/log/messages > $filename  
  17 echo "$filename contains tail end of system log."
  18 
  19 exit 0

3.5.2. Comparison operators (binary)

integer comparison

-eq

is equal to ($a -eq $b)

-ne

is not equal to ($a -ne $b)

-gt

is greater than ($a -gt $b)

-ge

is greater than or equal to ($a -ge $b)

-lt

is less than ($a -lt $b)

-le

is less than or equal to ($a -le $b)

string comparison

=

is equal to ($a = $b)

!=

is not equal to ($a != $b)

\<

is less than, in ASCII alphabetical order ($a \< $b)

Note that the "<" needs to be escaped.

\>

is greater than, in ASCII alphabetical order ($a \> $b)

Note that the ">" needs to be escaped.

See Example 3-91 for an application of this comparison operator.

-z

string is "null", that is, has zero length

-n

string is not "null".

Caution

This test requires that the string be quoted within the test brackets. You may use ! -z instead, or even just the string itself, without a test operator (see Example 3-13).


Example 3-12. arithmetic and string comparisons

   1 #!/bin/bash
   2 
   3 a=4
   4 b=5
   5 
   6 # Here a and b can be treated either as integers or strings.
   7 # There is some blurring between the arithmetic and integer comparisons.
   8 # Be careful.
   9 
  10 if [ $a -ne $b ]
  11 then
  12   echo "$a is not equal to $b"
  13   echo "(arithmetic comparison)"
  14 fi
  15 
  16 echo
  17 
  18 if [ $a != $b ]
  19 then
  20   echo "$a is not equal to $b."
  21   echo "(string comparison)"
  22 fi
  23 
  24 echo
  25 
  26 exit 0


Example 3-13. testing whether a string is null

   1 #!/bin/bash
   2 
   3 # If a string has not been initialized, it has no defined value.
   4 # This state is called "null" (not the same as zero).
   5 
   6 
   7 if [ -n $string1 ]   # $string1 has not been declared or initialized.
   8 then
   9   echo "String \"string1\" is not null."
  10 else  
  11   echo "String \"string1\" is null."
  12 fi  
  13 # Wrong result.
  14 # Shows $string1 as not null, although it was not initialized.
  15 
  16 echo
  17 
  18 # Lets try it again.
  19 
  20 if [ -n "$string1" ]  # This time, $string1 is quoted.
  21 then
  22   echo "String \"string1\" is not null."
  23 else  
  24   echo "String \"string1\" is null."
  25 fi  
  26 
  27 echo
  28 
  29 if [ $string1 ]  # This time, $string1 stands naked.
  30 then
  31   echo "String \"string1\" is not null."
  32 else  
  33   echo "String \"string1\" is null."
  34 fi  
  35 # This works fine.
  36 # The [ ] test operator alone detects whether the string is null.
  37 
  38 echo
  39 
  40 string1=initialized
  41 
  42 if [ $string1 ]  # This time, $string1 stands naked.
  43 then
  44   echo "String \"string1\" is not null."
  45 else  
  46   echo "String \"string1\" is null."
  47 fi  
  48 # Again, gives correct result.
  49 
  50 
  51 exit 0
  52 
  53 # Thanks to Florian Wisser for pointing this out.


Example 3-14. zmost

   1 #!/bin/bash
   2 
   3 #View gzipped files with 'most'
   4 
   5 NOARGS=1
   6 
   7 if [ $# = 0 ]
   8 # same effect as:  if [ -z $1 ]
   9 then
  10   echo "Usage: `basename $0` filename" >&2
  11   # Error message to stderr.
  12   exit $NOARGS
  13   # Returns 1 as exit status of script
  14   # (error code)
  15 fi  
  16 
  17 filename=$1
  18 
  19 if [ ! -f $filename ]
  20 then
  21   echo "File $filename not found!" >&2
  22   # Error message to stderr.
  23   exit 2
  24 fi  
  25 
  26 if [ ${filename##*.} != "gz" ]
  27 # Using bracket in variable substitution.
  28 then
  29   echo "File $1 is not a gzipped file!"
  30   exit 3
  31 fi  
  32 
  33 zcat $1 | most
  34 
  35 exit 0
  36 
  37 # Uses the file viewer 'most'
  38 # (similar to 'less')

compound comparison

-a

logical and

exp1 -a exp2 returns true if both exp1 and exp2 are true.

-o

logical or

exp1 -o exp2 returns true if either exp1 or exp2 are true.

These are simpler forms of the comparison operators && and ||, which require brackets to separate the target expressions.

Refer to Example 3-15 to see compound comparison operators in action.