### Combining Expressions

It’s also possible to combine expressions to create more complex evaluations. Expres- sions are combined by using logical operators. We saw these in Chapter 17, when we learned about the find command. There are three logical operations for test and [[ ]]. They are AND, OR and NOT. test and [[ ]] use different operators to repre- sent these operations :

Table 27-4: Logical Operators

#### Operation test [[ ]] and (( ))

AND -a &&

OR -o ||

NOT ! !

Here’s an example of an AND operation. The following script determines if an integer is within a range of values:

#!/bin/bash

# test-integer3: determine if an integer is within a

# specified range of values.

MIN_VAL=1 MAX_VAL=100

INT=50

#!/bin/bash

# test-integer3: determine if an integer is within a

# specified range of values.

MIN_VAL=1 MAX_VAL=100

INT=50

if [[ "\$INT" =~ ^-?[0-9]+\$ ]]; then

if [[ INT -ge MIN_VAL && INT -le MAX_VAL ]]; then echo "\$INT is within \$MIN_VAL to \$MAX_VAL."

else

echo "\$INT is out of range."

fi else

echo "INT is not an integer." >&2 exit 1

fi

if [[ "\$INT" =~ ^-?[0-9]+\$ ]]; then

if [[ INT -ge MIN_VAL && INT -le MAX_VAL ]]; then echo "\$INT is within \$MIN_VAL to \$MAX_VAL."

else

echo "\$INT is out of range."

fi else

echo "INT is not an integer." >&2 exit 1

fi

In this script, we determine if the value of integer INT lies between the values of MIN_VAL and MAX_VAL. This is performed by a single use of [[ ]], which includes two expressions separated by the && operator. We could have also coded this using test:

if [ \$INT -ge \$MIN_VAL -a \$INT -le \$MAX_VAL ]; then echo "\$INT is within \$MIN_VAL to \$MAX_VAL."

else

echo "\$INT is out of range."

fi

if [ \$INT -ge \$MIN_VAL -a \$INT -le \$MAX_VAL ]; then echo "\$INT is within \$MIN_VAL to \$MAX_VAL."

else

echo "\$INT is out of range."

fi

The ! negation operator reverses the outcome of an expression. It returns true if an ex- pression is false, and it returns false if an expression is true. In the following script, we modify the logic of our evaluation to find values of INT that are outside the specified range:

#!/bin/bash

# test-integer4: determine if an integer is outside a

# specified range of values.

MIN_VAL=1 MAX_VAL=100

INT=50

if [[ "\$INT" =~ ^-?[0-9]+\$ ]]; then

if [[ ! (INT -ge MIN_VAL && INT -le MAX_VAL) ]]; then echo "\$INT is outside \$MIN_VAL to \$MAX_VAL."

else

echo "\$INT is in range."

#!/bin/bash

# test-integer4: determine if an integer is outside a

# specified range of values.

MIN_VAL=1 MAX_VAL=100

INT=50

if [[ "\$INT" =~ ^-?[0-9]+\$ ]]; then

if [[ ! (INT -ge MIN_VAL && INT -le MAX_VAL) ]]; then echo "\$INT is outside \$MIN_VAL to \$MAX_VAL."

else

echo "\$INT is in range."

echo "INT is not an integer." >&2 exit 1

fi

echo "INT is not an integer." >&2 exit 1

fi

We also include parentheses around the expression, for grouping. If these were not in- cluded, the negation would only apply to the first expression and not the combination of the two. Coding this with test would be done this way:

if [ ! \( \$INT -ge \$MIN_VAL -a \$INT -le \$MAX_VAL \) ]; then echo "\$INT is outside \$MIN_VAL to \$MAX_VAL."

else

echo "\$INT is in range."

fi

if [ ! \( \$INT -ge \$MIN_VAL -a \$INT -le \$MAX_VAL \) ]; then echo "\$INT is outside \$MIN_VAL to \$MAX_VAL."

else

echo "\$INT is in range."

fi

Since all expressions and operators used by test are treated as command arguments by the shell (unlike [[ ]] and (( )) ), characters which have special meaning to bash, such as <, >, (, and ), must be quoted or escaped.

Seeing that test and [[ ]] do roughly the same thing, which is preferable? test is traditional (and part of POSIX), whereas [[ ]] is specific to bash. It’s important to know how to use test, since it is very widely used, but [[ ]] is clearly more useful and is easier to code, so it is preferred for modern scripts.

### Portability Is The Hobgoblin Of Little Minds

If you talk to “real” Unix people, you quickly discover that many of them don’t like Linux very much. They regard it as impure and unclean. One tenet of Unix users is that everything should be “portable.” This means that any script you write should be able to run, unchanged, on any Unix-like system.

Unix people have good reason to believe this. Having seen what proprietary ex- tensions to commands and shells did to the Unix world before POSIX, they are naturally wary of the effect of Linux on their beloved OS.

But portability has a serious downside. It prevents progress. It requires that things are always done using “lowest common denominator” techniques. In the case of shell programming, it means making everything compatible with sh, the original Bourne shell.

This downside is the excuse that proprietary software vendors use to justify their proprietary extensions, only they call them “innovations.” But they are really just lock-in devices for their customers.

The GNU tools, such as bash, have no such restrictions. They encourage porta- bility by supporting standards and by being universally available. You can install bash and the other GNU tools on almost any kind of system, even Windows, without cost. So feel free to use all the features of bash. It’s really portable.