Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting
Prev | Chapter 7. Tests | Next

# 7.3. Other Comparison Operators

A binary comparison operator compares two variables or quantities. Note that integer and string comparison use a different set of operators.

integer comparison

-eq

  is equal to

  `if [ "$a" -eq "$b" ]`

-ne

  is not equal to

  `if [ "$a" -ne "$b" ]`

-gt

  is greater than

  `if [ "$a" -gt "$b" ]`

-ge

  is greater than or equal to

  `if [ "$a" -ge "$b" ]`

-lt

  is less than

  `if [ "$a" -lt "$b" ]`

-le

  is less than or equal to

  `if [ "$a" -le "$b" ]`

<

  is less than (within double parentheses)

  `(("$a" < "$b"))`

<=

  is less than or equal to (within double parentheses)

  `(("$a" <= "$b"))`

>

  is greater than (within double parentheses)

  `(("$a" > "$b"))`

>=

  is greater than or equal to (within double parentheses)

  `(("$a" >= "$b"))`

string comparison

=

  is equal to

  `if [ "$a" = "$b" ]`

  | Note the whitespace framing the =. `if [ "$a"="$b" ]` is not equivalent to the above.

==

  is equal to

  `if [ "$a" == "$b" ]`

  This is a synonym for =.

  The == comparison operator behaves differently within a double-brackets test than within single brackets.

         1 [[ $a == z* ]]   # True if $a starts with an "z" (pattern matching).
         2 [[ $a == "z*" ]] # True if $a is equal to z* (literal matching).
         3
         4 [ $a == z* ]     # File globbing and word splitting take place.
         5 [ "$a" == "z*" ] # True if $a is equal to z* (literal matching).
         6
         7 # Thanks, Stéphane Chazelas

!=

  is not equal to

  `if [ "$a" != "$b" ]`

  This operator uses pattern matching within a [[ ... ]] construct.

<

  is less than, in ASCII alphabetical order

  `if [[ "$a" < "$b" ]]`

  `if [ "$a" \< "$b" ]`

  Note that the "<" needs to be escaped within a `[ ]` construct.

>

  is greater than, in ASCII alphabetical order

  `if [[ "$a" > "$b" ]]`

  `if [ "$a" \> "$b" ]`

  Note that the ">" needs to be escaped within a `[ ]` construct.

  See Example 27-11 for an application of this comparison operator.

-z

  string is null, that is, has zero length

         1  String=''   # Zero-length ("null") string variable.
         2
         3 if [ -z "$String" ]
         4 then
         5   echo "\$String is null."
         6 else
         7   echo "\$String is NOT null."
         8 fi     # $String is null.

-n

  string is not null.

  | The `-n` test requires that the string be quoted within the test brackets. Using an unquoted string with ! -z, or even just the unquoted string alone within test brackets (see Example 7-6) normally works, however, this is an unsafe practice. Always quote a tested string. [1]

Example 7-5. 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 string comparisons,
       8 #+ since Bash variables are not strongly typed.
       9
      10 #  Bash permits integer operations and comparisons on variables
      11 #+ whose value consists of all-integer characters.
      12 #  Caution advised, however.
      13
      14 echo
      15
      16 if [ "$a" -ne "$b" ]
      17 then
      18   echo "$a is not equal to $b"
      19   echo "(arithmetic comparison)"
      20 fi
      21
      22 echo
      23
      24 if [ "$a" != "$b" ]
      25 then
      26   echo "$a is not equal to $b."
      27   echo "(string comparison)"
      28   #     "4"  != "5"
      29   # ASCII 52 != ASCII 53
      30 fi
      31
      32 # In this particular instance, both "-ne" and "!=" work.
      33
      34 echo
      35
      36 exit 0

Example 7-6. Testing whether a string is null

       1 #!/bin/bash
       2 #  str-test.sh: Testing null strings and unquoted strings,
       3 #+ but not strings and sealing wax, not to mention cabbages and kings . . .
       4
       5 # Using   if [ ... ]
       6
       7 # If a string has not been initialized, it has no defined value.
       8 # This state is called "null" (not the same as zero!).
       9
      10 if [ -n $string1 ]    # string1 has not been declared or initialized.
      11 then
      12   echo "String \"string1\" is not null."
      13 else
      14   echo "String \"string1\" is null."
      15 fi                    # Wrong result.
      16 # Shows $string1 as not null, although it was not initialized.
      17
      18 echo
      19
      20 # Let's try it again.
      21
      22 if [ -n "$string1" ]  # This time, $string1 is quoted.
      23 then
      24   echo "String \"string1\" is not null."
      25 else
      26   echo "String \"string1\" is null."
      27 fi                    # Quote strings within test brackets!
      28
      29 echo
      30
      31 if [ $string1 ]       # This time, $string1 stands naked.
      32 then
      33   echo "String \"string1\" is not null."
      34 else
      35   echo "String \"string1\" is null."
      36 fi                    # This works fine.
      37 # The [ ... ] test operator alone detects whether the string is null.
      38 # However it is good practice to quote it (if [ "$string1" ]).
      39 #
      40 # As Stephane Chazelas points out,
      41 #    if [ $string1 ]    has one argument, "]"
      42 #    if [ "$string1" ]  has two arguments, the empty "$string1" and "]"
      43
      44
      45 echo
      46
      47
      48 string1=initialized
      49
      50 if [ $string1 ]       # Again, $string1 stands unquoted.
      51 then
      52   echo "String \"string1\" is not null."
      53 else
      54   echo "String \"string1\" is null."
      55 fi                    # Again, gives correct result.
      56 # Still, it is better to quote it ("$string1"), because . . .
      57
      58
      59 string1="a = b"
      60
      61 if [ $string1 ]       # Again, $string1 stands unquoted.
      62 then
      63   echo "String \"string1\" is not null."
      64 else
      65   echo "String \"string1\" is null."
      66 fi                    # Not quoting "$string1" now gives wrong result!
      67
      68 exit 0   # Thank you, also, Florian Wisser, for the "heads-up".

Example 7-7. zmore

       1 #!/bin/bash
       2 # zmore
       3
       4 # View gzipped files with 'more' filter.
       5
       6 E_NOARGS=85
       7 E_NOTFOUND=86
       8 E_NOTGZIP=87
       9
      10 if [ $# -eq 0 ] # same effect as:  if [ -z "$1" ]
      11 # $1 can exist, but be empty:  zmore "" arg2 arg3
      12 then
      13   echo "Usage: `basename $0` filename" >&2
      14   # Error message to stderr.
      15   exit $E_NOARGS
      16   # Returns 85 as exit status of script (error code).
      17 fi
      18
      19 filename=$1
      20
      21 if [ ! -f "$filename" ]   # Quoting $filename allows for possible spaces.
      22 then
      23   echo "File $filename not found!" >&2   # Error message to stderr.
      24   exit $E_NOTFOUND
      25 fi
      26
      27 if [ ${filename##*.} != "gz" ]
      28 # Using bracket in variable substitution.
      29 then
      30   echo "File $1 is not a gzipped file!"
      31   exit $E_NOTGZIP
      32 fi
      33
      34 zcat $1 | more
      35
      36 # Uses the 'more' filter.
      37 # May substitute 'less' if desired.
      38
      39 exit $?   # Script returns exit status of pipe.
      40 #  Actually "exit $?" is unnecessary, as the script will, in any case,
      41 #+ return the exit status of the last command executed.

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 is true.

These are similar to the Bash comparison operators && and ||, used within double brackets.

       1 [[ condition1 && condition2 ]]

The -o and -a operators work with the test command or occur within single test brackets.

       1 if [ "$expr1" -a "$expr2" ]
       2 then
       3   echo "Both expr1 and expr2 are true."
       4 else
       5   echo "Either expr1 or expr2 is false."
       6 fi

But, as rihad points out:

       1 [ 1 -eq 1 ] && [ -n "`echo true 1>&2`" ]   # true
       2 [ 1 -eq 2 ] && [ -n "`echo true 1>&2`" ]   # (no output)
       3 # ^^^^^^^ False condition. So far, everything as expected.
       4
       5 # However ...
       6 [ 1 -eq 2 -a -n "`echo true 1>&2`" ]       # true
       7 # ^^^^^^^ False condition. So, why "true" output?
       8
       9 # Is it because both condition clauses within brackets evaluate?
      10 [[ 1 -eq 2 && -n "`echo true 1>&2`" ]]     # (no output)
      11 # No, that's not it.
      12
      13 # Apparently && and || "short-circuit" while -a and -o do not.

Refer to Example 8-3, Example 27-17, and Example A-29 to see compound comparison operators in action.

### Notes

[1] | As S.C. points out, in a compound test, even quoting the string variable might not suffice. `[ -n "$string" -o "$a" = "$b" ]` may cause an error with some versions of Bash if `$string` is empty. The safe way is to append an extra character to possibly empty variables, `[ "x$string" != x -o "x$a" = "x$b" ]` (the "x's" cancel out).

Prev | Home | Next
File test operators | Up | Nested `if/then` Condition Tests