<span class="latin" style="width:19px;height:19px;">T</span><span class="latin" style="width:19px;height:19px;">e</span><span class="latin" style="width:19px;height:19px;">s</span><span class="latin" style="width:19px;height:19px;">t</span><span class="latin" style="display:block;width:19px;height:19px;"> </span><span class="latin" style="width:19px;height:19px;">C</span><span class="latin" style="width:19px;height:19px;">o</span><span class="latin" style="width:19px;height:19px;">n</span><span class="latin" style="width:19px;height:19px;">s</span><span class="latin" style="width:19px;height:19px;">t</span><span class="latin" style="width:19px;height:19px;">r</span><span class="latin" style="width:19px;height:19px;">u</span><span class="latin" style="width:19px;height:19px;">c</span><span class="latin" style="width:19px;height:19px;">t</span><span class="latin" style="width:19px;height:19px;">s</span>
Advanced Bash-Scripting Guide:
PrevChapter 7. TestsNext


7.1. Test Constructs

Anif/then construct tests whether the  exit status of a list  of commands is 0 (since 0 means  "success" by UNIX convention), and if so, executes  one or more commands.
There exists a dedicated command called [ (left bracket  special character). It is a synonym for test,  and a builtin for efficiency  reasons. This command considers its arguments as comparison  expressions or file tests and returns an exit status corresponding  to the result of the comparison (0 for true, 1 for false).
With version 2.02, Bash introduced the [[ ... ]] extended  test command, which performs comparisons  in a manner more familiar to programmers from other  languages. Note that [[ is a keyword, not a command.
Bash sees [[ $a -lt $b ]] as a  single element, which returns an exit status.
The (( ... )) and let ... constructs return an  exit status,  according to whether the arithmetic expressions they  evaluate expand to a non-zero value. These  arithmetic-expansion  constructs may therefore be used to perform arithmetic comparisons.
(( 0 &&1))                 # Logical AND
echo $?     # 1     ***
# And so ...
let "num = (( 0 &&1))"
echo $num   # 0
# But ...
let "num = (( 0 &&1))"
echo $?     # 1     ***


(( 200 || 11 ))              # Logical OR
echo $?     # 0     ***
# ...
let "num = (( 200 || 11 ))"
echo $num   # 1
let "num = (( 200 || 11 ))"
echo $?     # 0     ***


(( 200 | 11 ))               # Bitwise OR
echo $?                      # 0     ***
# ...
let "num = (( 200 | 11 ))"
echo $num                    # 203
let "num = (( 200 | 11 ))"
echo $?                      # 0     ***

# The "let" construct returns the same exit status
#+ as the double-parentheses arithmetic expansion.

Caution

Again, note that the exit status of an arithmetic expression is not an error value.
var=-2 && (( var+=2 ))
echo $?                   # 1

var=-2 && (( var+=2 )) && echo $var
                          # Will not echo $var!

Anifcan test any command, not just  conditions enclosed within brackets.
if cmp a b &> /dev/null  # Suppress output.
then echo "Files a and b are identical."
else echo "Files a and b differ."
fi

# The very useful "if-grep" construct:
# ----------------------------------- 
if grep -q Bash file
  then echo "File contains at least one occurrence of Bash."
fi

word=Linux
letter_sequence=inu
if echo "$word" | grep -q "$letter_sequence"
# The "-q" option to grep suppresses output.
then
  echo "$letter_sequence found in $word"
else
  echo "$letter_sequence not found in $word"
fi


if COMMAND_WHOSE_EXIT_STATUS_IS_0_UNLESS_ERROR_OCCURRED
  then echo "Command succeeded."
  else echo "Command failed."
fi
 
These last two examples  courtesy of Sthane Chazelas.

Example 7-1. What is truth?
#!/bin/bash

#  Tip:
#  If you're unsure how a certain condition might evaluate,
#+ test it in an if-test.

echo

echo "Testing \"0\""
if [ 0 ]      # zero
then
  echo "0 is true."
else          # Or else ...
  echo "0 is false."
fi            # 0 is true.

echo

echo "Testing \"1\""
if [1]      # one
then
  echo "1 is true."
else
  echo "1 is false."
fi            # 1 is true.

echo

echo "Testing \"-1\""
if [ -1 ]     # minus one
then
  echo "-1 is true."
else
  echo "-1 is false."
fi            # -1 is true.

echo

echo "Testing \"NULL\""
if [ ]        # NULL (empty condition)
then
  echo "NULL is true."
else
  echo "NULL is false."
fi            # NULL is false.

echo

echo "Testing \"xyz\""
if [ xyz ]    # string
then
  echo "Random string is true."
else
  echo "Random string is false."
fi            # Random string is true.

echo

echo "Testing \"\$xyz\""
if [ $xyz ]   # Tests if $xyz is null, but...
              # it's only an uninitialized variable.
then
  echo "Uninitialized variable is true."
else
  echo "Uninitialized variable is false."
fi            # Uninitialized variable is false.

echo

echo "Testing \"-n \$xyz\""
if [ -n "$xyz" ]            # More pedantically correct.
then
  echo "Uninitialized variable is true."
else
  echo "Uninitialized variable is false."
fi            # Uninitialized variable is false.

echo


xyz=          # Initialized, but set to null value.

echo "Testing \"-n \$xyz\""
if [ -n "$xyz" ]
then
  echo "Null variable is true."
else
  echo "Null variable is false."
fi            # Null variable is false.


echo


# When is "false" true?

echo "Testing \"false\""
if [ "false" ]              #  It seems that "false" is just a string ...
then
  echo "\"false\" is true." #+ and it tests true.
else
  echo "\"false\" is false."
fi            # "false" is true.

echo

echo "Testing \"\$false\""  # Again, uninitialized variable.
if [ "$false" ]
then
  echo "\"\$false\" is true."
else
  echo "\"\$false\" is false."
fi            # "$false" is false.
              # Now, we get the expected result.

#  What would happen if we tested the uninitialized variable "$true"?

echo

exit 0
Exercise. Explain the behavior of Example 7-1, above.
if [ condition-true ]
then
   command 1
   command 2
   ...
else  # Or else ...
      # Adds default code block executing if original condition tests false.
   command 3
   command 4
   ...
fi
 
Note

When if and then are on same line in a condition test, a semicolon must terminate the if statement. Both if and then are keywords. Keywords (or commands) begin statements, and before a new statement on the same line begins, the old one must terminate.

if [ -x "$filename" ]; then

Else if and elif
elif
elif is a contraction  for else if. The effect is to nest an  inner if/then construct within an outer  one.
if [ condition1 ]
then
   command1
   command2
   command3
elif [ condition2 ]
# Same as else if
then
   command4
   command5
else
   default-command
fi
 
The if test condition-true construct is the  exact equivalent of if [ condition-true ].  As it happens, the left bracket, [ , is a  token  [1]  which invokes the test command. The closing  right bracket, ] , in an if/test should not  therefore be strictly necessary, however newer versions of Bash  require it.
Note

The test command is a Bash builtin which tests file types and compares strings. Therefore, in a Bash script, test does not call the external /usr/bin/test binary, which is part of the sh-utils package. Likewise, [ does not call /usr/bin/[, which is linked to /usr/bin/test.

bash$ type test
test is a shell builtin
bash$ type '['
[ is a shell builtin
bash$ type '[['
[[ is a shell keyword
bash$ type ']]'
]] is a shell keyword
bash$ type ']'
bash: type: ]: not found
       

If, for some reason, you wish to use /usr/bin/test in a Bash script, then specify it by full pathname.


Example 7-2. Equivalence of test,  /usr/bin/test, [ ],  and /usr/bin/[
#!/bin/bash

echo

if test -z "$1"
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if /usr/bin/test -z "$1"      # Equivalent to "test" builtin.
#  ^^^^^^^^^^^^^              # Specifying full pathname.
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

if [ -z "$1" ]                # Functionally identical to above code blocks.
#   if [ -z "$1"                should work, but...
#+  Bash responds to a missing close-bracket with an error message.
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo


if /usr/bin/[ -z "$1" ]       # Again, functionally identical to above.
# if /usr/bin/[ -z "$1"       # Works, but gives an error message.
#                             # Note:
#                               This has been fixed in Bash, version 3.x.
then
  echo "No command-line arguments."
else
  echo "First command-line argument is $1."
fi

echo

exit 0
Note

Following an if, neither the test command nor the test brackets ( [ ] or [[ ]] ) are strictly necessary.
dir=/home/bozo

if cd "$dir" 2>/dev/null; then   # "2>/dev/null" hides error message.
  echo "Now in $dir."
else
  echo "Can't change to $dir."
fi
The "if COMMAND" construct returns the exit status of COMMAND.

Similarly, a condition within test brackets may stand alone without an if, when used in combination with a list construct.
var1=20
var2=22
[ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"

home=/home/bozo
[ -d "$home" ] || echo "$home directory does not exist."

The (( ))  construct expands and evaluates an arithmetic  expression. If the expression evaluates as zero, it returns  an exit statusof1, or "false". A non-zero  expression returns an exit status of 0,  or "true". This is in marked contrast to using  the test and [ ] constructs  previously discussed.

Example 7-3. Arithmetic Tests using (( ))
#!/bin/bash
# arith-tests.sh
# Arithmetic tests.

# The (( ... )) construct evaluates and tests numerical expressions.
# Exit status opposite from [ ... ] construct!

(( 0 ))
echo "Exit status of \"(( 0 ))\" is $?."         # 1

((1))
echo "Exit status of \"((1))\" is $?."         # 0

(( 5 >4))                                      # true
echo "Exit status of \"(( 5 >4))\" is $?."     # 0

(( 5 >9))                                      # false
echo "Exit status of \"(( 5 >9))\" is $?."     # 1

(( 5 == 5 ))                                     # true
echo "Exit status of \"(( 5 == 5 ))\" is $?."    # 0
# (( 5 = 5 ))  gives an error message.

(( 5 - 5 ))                                      # 0
echo "Exit status of \"(( 5 - 5 ))\" is $?."     # 1

(( 5 / 4 ))                                      # Division o.k.
echo "Exit status of \"(( 5 / 4 ))\" is $?."     # 0

(( 1 / 2 ))                                      # Division result < 1.
echo "Exit status of \"(( 1 / 2 ))\" is $?."     # Rounded off to 0.
                                                 # 1

(( 1 / 0 )) 2>/dev/null                          # Illegal division by 0.
#           ^^^^^^^^^^^
echo "Exit status of \"(( 1 / 0 ))\" is $?."     # 1

# What effect does the "2>/dev/null" have?
# What would happen if it were removed?
# Try removing it, then rerunning the script.

# ======================================= #

# (( ... )) also useful in an if-then test.

var1=5
var2=4

if (( var1 > var2 ))
then #^      ^      Note: Not $var1, $var2. Why?
  echo "$var1 is greater than $var2"
fi     # 5 is greater than 4

exit 0

Notes

[1]

Atoken is a symbol or short string with a special meaning attached to it (ameta-meaning). In Bash, certain tokens, such as [ and . (dot-command), may expand to keywords and commands.



PrevHomeNext
TestsUpFile test operators