It is said that the beauty of Unix standards comes from their wide variety, and the logical expressions and tests in Linux scripting prove this aphorism.
A word about programming logic. Typically, logic tests produce one of two results: true (1), or false (0). (Right here, those old FORTRAN geezers can stop screaming about arithmetic if.) The operators in a logic test are mostly either unary or binary, meaning that a test either evaluates a condition of one term, or compares two terms. Lets examine what this means.
File entries may point to either directories or data. The unary operator -d, when applied to a file, returns true if the entry is a directory, and false if it is some data file type. Now suppose the file in question does not exist; what should the operator return? Well, a non-entry for a file clearly is not a directory, so the test returns false. (Another operator, -e, would test whether the file exists.) So: a unary operator should return true or false with respect to a single condition only.
Comparison operators are binary, meaning that two terms must be compared. These tests might be thought of as arithmetic in nature, even when applied to strings of text. It is easy to determine if one string equals another, but what does it mean for one string to be greater than another? Do we compare the lengths of the strings, or their alphabetic order? We compare alphabetic ordering.
One might suppose that, having defined the meaning of comparisons, only one set of comparison operators would be needed, and there would be one way to perform a logic test. Well, that is not shell scripting.
There are multiple ways to construct a logical test expression:
if ... | if uses the exit status of any expression(s) |
[ or
test
|
built-in logical evaluation commands
lexicographical string comparisons using ASCII ordering
|
[[ ... ]]
|
keyword for extended test command
Uses lexicographical language order of locale
|
Tip
To be certain of how an expression will evaluate, test it in an if ... then statement:
if expression(s)
then echo "Result true"
else echo "Result false"
fi
Rules apply to tests and comparisons. Not every situation requires each rule, as there are many exceptions. But failure to use these conventions will certainly lead to trouble when it is least expected.
Warning
Some operators, notably != and ==, have rather different meanings when used within [ rather than [[. This may produce confusing results when comparing variables which evaluate to integers. BASH variables are vaguely typed, and variables may be evaluated (integer) or not (string) depending on how the variables are quoted. On top of this, strings may be compared literally or by pattern matching, depending on which test is used.
See also
On-line bash documentation, man bash.
Logic is only useful when it is applied to decision making. (Back in the FORTRAN days this was called flow control, although it may have a different name now.) Bash has six compound expression forms which perform iteration or branching based on logic or decisions, as shown following:
Bash compound expressions syntax |
---|
for name [ [ in [ word ... ] ] ; ] do list ; done |
for (( expr1 ; expr2 ; expr3 )) ; do list ; done |
select name [ in word ] ; do list ; done |
case word in [ [(] pattern [ | pattern ] ... ) list ;; ] ... esac |
if list; then list; [ elif list; then list; ] ... [ else list; ] fi |
while list-1; do list-2; done
until list-1; do list-2; done
|
for name [ in [ word ... ] ]
do
command list
done
The word list following in is expanded, generating a list of items. The variable name is set to each element of the expanded word list in turn, and the command list is executed for each turn.
Tip
for (( expression1 ; expression2 ; expression3 ))
do
command list
done
Arithmetic expression1 is evaluated by the rules of Arithmetic Evaluation. Then arithmetic expression2 is evaluated repeatedly until it evaluates to 0. Each time expression2 evaluates to a non-zero value, the command list is executed and arithmetic expression3 is evaluated. If any expression is omitted, it behaves as if it evaluates to 1.
select name [ in word ]
do
command list
done
The list of words following in is expanded, generating a list of items. The set of expanded words is printed on the standard error, each preceded by a number. If the in word is omitted, the positional parameters are printed (see PARAMETERS below). The PS3 prompt is then displayed and a line read from the standard input. If the line consists of a number corresponding to one of the displayed words, then the value of name is set to that word. If the line is empty, the words and prompt are displayed again. If EOF is read, the command completes. Any other value read causes name to be set to null. The line read is saved in the variable REPLY. The list is executed after each selection until a break command is executed.
case word in
pattern|pattern)
command list ;;
pattern2|pattern2)
command list ;;
*)
command list ;;
esac
A case command first expands word (see note 1), and tries to match it against each pattern in turn, using pathname pattern matching rules (see note 2). Once the first match is found, the associated list of commands is executed, up to the termination operator, which is processed as follows:
Operator | Termination result |
---|---|
;; | The case statement exits at esac. |
;& | Execution continues with the command list of next code block. |
;;& | Pattern match testing continues with the next code block. |
Tip
if expression list
then
command list ;
elif expression list
then
command list ;
...
else expression list
command list ;
fi
The if test expression list is executed. If its exit status is 0, the then list is executed. Otherwise, each elif test list is executed in turn, and if its exit status is 0, the corresponding then list is executed and the command completes. Otherwise, the else list is executed, if present, to complete the execution.
while expression list
do
command list
done
until expression list
do
command list
done
The while command continuously executes the command list as long as the last statement in the expression list returns an exit status of 0. The until command is identical to the while command, except that the test is negated; the command list is executed as long as the last statement in the test expression list returns a non-zero exit status.
Note
See also
online manual, terminal command man bash.