An if/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 also return an
exit status of 0 if the arithmetic
expressions they evaluate expand to a non-zero value. These
arithmetic expansion
constructs may therefore be used to perform arithmetic
comparisons.
let "1<2" returns 0 (as "1<2" expands to "1")
(( 0 && 1 )) returns 1 (as "0 && 1" expands to "0")
An if can 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
An if/then construct can contain nested
comparisons and tests.
if echo "Next *if* is part of the comparison for the first *if*."
if [[ $comparison = "integer" ]]
then (( a < b ))
else
[[ $a < $b ]]
fi
then
echo '$a is less than $b'
fi
This detailed "if-test" explanation
courtesy of St�phane Chazelas.
Example 7-1. What is truth?
#!/bin/bash
# Tip:
# If you're unsure of how a certain condition would evaluate,
#+ test it in an if-test.
echo
echo "Testing \"0\""
if [ 0 ] # zero
then
echo "0 is true."
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
# Optional (may be left out if not needed).
# Adds default code block executing if original condition tests false.
command 3
command 4
...
fi
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
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.
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 testtest is a shell builtinbash$ type '['[ is a shell builtinbash$ type '[['[[ is a shell keywordbash$ type ']]']] is a shell keywordbash$ type ']'bash: type: ]: not found
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" # Same result as "test" builtin".
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
The [[ ]] construct
is the more versatile Bash version of [ ]. This
is the extended test command, adopted from
ksh88.
No filename expansion or word splitting takes place
between [[ and ]], but there is
parameter expansion and command substitution.
file=/etc/passwd
if [[ -e $file ]]
then
echo "Password file exists."
fi
Using the [[ ... ]] test construct,
rather than [ ... ] can prevent many
logic errors in scripts. For example, the &&,
||, <, and >
operators work within a [[ ]] test, despite
giving an error within a [ ] construct.
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 status of
1, 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
# 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 )) # 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.
exit 0