Project81.Write Complex Bash Conditions


Project 81. Write Complex Bash Conditions

"How does Bash interpret conditional expressions?"

This project looks at the many forms of conditional expression supported by Bash. It explains the differences of the forms and compares them with one another. It also presents some handy tricks and gives tips on how to avoid syntax errors and malformed conditions.

Learn More

Project 10 introduces basic shell scripting techniques and discusses the conditional statements supported by Bash.


Understand Bash Conditions

Bash supports conditional expressions that are used in conditional statements such as if, while, and until. Here's an example in which we test whether 5 is less than 7 (we use -lt to mean less than). The condition is enclosed in [...] and evaluates to true or false. Ideally, Bash will find truth in such a condition.

$ if [ 5 -lt 7 ]; then echo "yes"; else echo "no"; fi yes


Now let's examine this simple expression in more detail to discover how Bash interprets it, and explore the alternative forms of conditional expression offered by Bash.

There is more to Bash conditional expressions than is at first apparent. Let's look at how Bash interprets a conditional expression. This is key to understanding the different forms and being able to make the most of them.

When interpreting a conditional statement such as if, Bash does not expect to see a Boolean value (TRUE or FALSE), as other languages do, such as C and PHP. Rather, Bash expects to see an executable command. The syntax is effectively

if command; then...


Within such a command line, Bash executes the command that follows if and replaces it with whatever value the command returns. A return value of 0 is interpreted as TRUE; any other return value is interpreted as FALSE.

The [ Command

In our example statement, you might well ask about the whereabouts of the command Bash requires following if. The answer is a little surprising: Bracket ([) is actually a built-in Bash command. When interpreting a conditional statement such as

if [ 5 -lt 7 ]; then...


Tip

Just as for any other command, white space must separate [ and each of its parameters. You'll get a syntax error, or a conditional expression that evaluates incorrectly, if you omit the white space. The final parameter, ], is required for syntactic completeness (or perhaps aesthetic value).


Bash first executes the bracket command, passing it the four parameters that form the remainder of the statement: 5, -lt, 7, and ]. (The statement is terminated by a semicolon.) The bracket command (not Bash command-line interpretation) evaluates the conditional expression and returns 0 if the statement is true (as it is in this case) and 1 if it is false.

Learn More

Refer to Project 16 for more information on the type command.


In our example, then, after it has executed the bracket command, Bash effectively sees the statement

if 0; then...


and interprets it as if TRUE; then....

We check the credentials of bracket with the type command.

$ type [ [ is a shell builtin


Equivalent to [ is the test command. The two are identical except that test does not expect to see a closing bracket.

$ if test 5 -lt 4; then echo "yes"; else echo "no"; fi no $ type test test is a shell builtin


To discover all the conditional operators supported by bracket and test, consult Bash's built-in help command by typing

$ help test


Learn More

Project 6 covers redirection and pipelining.


Several examples are given in the next section.

Here's a neat trick. A conditional statement may be given any command, not just [ or test. We could test whether two files differ by directly testing the return value from the diff command.

$ if diff eg1.txt eg2.txt &> /dev/null > then echo "Same"; else echo "Different"; fi Same


Most commands return 0 (trUE) for success or yes and 1 (FALSE) for failure or no. In the diff example, we took the precaution of throwing away all errors and other output by using the redirection &>/dev/null to prevent the shell script from writing unwanted text to the Terminal screen when it executes.

Tip

The return value of the last command to be executed is held in the special shell variable $?.

$ diff eg1.txt eg2.txt $ echo $? 0



Example Conditionals

The bracket command has a number of primaries you can use to test file attributes, such as whether a file exists.

$ if [ -e no-file ]; then echo "Exists"; ¬     else echo "No such file"; fi No such file


or whether you own a particular file.

$ if [ -O eg1.txt ]; then echo "It's mine"; fi It's mine


Bracket can compare strings for less than, greater than, equality, inequality, and emptiness. The next two examples demonstrate tests for equality and emptiness. The -z primary returns TRUE if the length of the string that follows is 0 (the string is empty).

$ ans="" $ if [ "$ans" = "yes" ] > then echo "You agree"; else echo "You disagree"; fi You disagree $ if [ -z "$ans" ]; then echo "You didn't reply"; fi You didn't reply


Integer evaluation is performed as demonstrated in previous examples, using -eq for equality, -ne for inequality, and so on. Type help test for more information.

Complex Conditions

You may specify more complex conditions by using AND, represented by -a; OR, represented by -o; and NOT, represented by !. We can test whether both the variables ans and default are empty by using the following complex condition.

$ if [ -z "$ans" -a -z "$default" ] > then echo "I don't know what you want"; fi


Don't omit the spaces between operators and operands. In the next example, we have omitted the spaces around the = sign.

$ allow=""; user="" $ if [ "$allow"="yes" -o "$user"="root" ]; ¬     then echo "OK"; fi OK


Omitting the spaces makes the conditional expression appear to be

[ "non-null-string" -o "non-null-string" ]


This is how it should look and evaluate.

$ if [ "$allow" = "yes" -o "$user" = "root" ]; ¬     then echo "OK"; fi $


We form expressions that are more complex by employing parentheses to ensure that evaluation occurs in the correct order. Our first attempt does not work.

$ ans="yes"; allow="no"; user="root" $ if [ "$ans" = "yes" -a ¬     ( "$allow" = "yes" -o "$user" = "root") ] -bash: syntax error near unexpected token `('


The syntax error is reported because the parentheses are parameters to the bracket command and must be escaped from the shell, as demonstrated in our next attempt.

$ if [ "$ans" = "yes" -a ¬     \("$allow" = "yes" -o "$user" = "root" \) ] > then echo "OK"; fi OK


Tip

It's fine to escape individual items within conditional expressions by enclosing them in quotes, but enclosing an entire expression in quotes will always cause it to be interpreted as a string with value TRUEa situation that can produce decidedly undesirable results.

$ if [ "1 -gt 9" ] > then echo "Odd"; fi Odd



Bash Boolean Operators

Compare the next two commands.

$ if [ "$allow" = "yes" -o "$user" = "root" ]; ¬     then echo "OK"; fi $ if [ "$allow" = "yes" ] || [ "$user" = "root" ]; ¬     then echo "OK"; fi


The difference between the two statements is that in the first example, the built-in bracket command evaluates the whole expression. In the second example, we have two separated bracket commands, and it's Bash that performs the OR operation, using its own || operator. The two commands are functionally equivalent; which you choose is a matter of personal preference. Bash uses a more friendly and C languagelike syntax. It provides OR (||), AND (&&), and NOT (!) operators.

Built-In and External Tests

Both of the Bash built-in commands test and [ have external equivalents in the directory /bin. These commands are used by older shells that do not have their own built-in equivalents.


We can employ Bash operators outside a conditional statement. For example:

$ command1 && command2


In such a command, command2 is executed if, and only if, command1 returns TRUE. This technique works because Bash does not evaluate the second part of an AND statement if the first part if FALSE; the result can only ever be FALSE. This behavior is known as short-circuiting. Similarly, we could specify

$ command1 || command2


In this example, command2 is executed if, and only if, command1 returns FALSE.

As a practical example, think of what happens if we type the following command line in a directory where no subdirectory named fred exists.

$ cd fred; ls -bash: cd: fred: No such file or directory Desktop     Library    Music        Public ...


Tip

To source a shell script if, and only if, it exists and is readable, use the following conditional syntax (shown here applied to an initialization script /sw/bin/init.sh).

[ -r /sw/bin/init.sh ] && source /sw/bin/init.sh



Command cd returns an error, but ls executes anyway, listing the current directory.

To avoid executing the ls command when the cd command fails, we use the following trick, which relies on the fact that cd returns TRUE when it succeeds and FALSE when it fails.

$ cd fred && ls -bash: cd: fred: No such file or directory $


Use the [[ Keyword

Bash provides a relatively new way of specifying a conditional expression, called an extended conditional expression. It uses the syntax [[...]] instead of [...] and is compatible with the older form. It is, in fact, a keyword like if and while, not a command like [ and cd, and suffers fewer limitations. It also uses the more friendly syntax && and || for AND and OR. We may type a conditional expression such as

$ if [[ "$allow" = "yes" || "$user" = "root" ]]; ¬     then echo "OK"; fi


Extended conditional expressions also spare you the trouble of escaping any parentheses they contain.

$ if [[ ("$allow" = "yes") || ("$user" = "root") ]]; ¬    then echo "OK"; fi


Note

The [[...]] construct was introduced in Bash 2.02.


Beware, however, that bare numbers within extended conditionals are treated as stringstext sequences without numerical value. You might be tempted to use this expression in the belief that Bash is employing integer arithmetic when evaluating the expression

$ if [[ (3 < 5) ]]; then echo "OK"; fi OK


Tip

To find out more about the [[...]] construct, type

$ help [[


or check the Bash man page by typing

$ man bash


and then type /\[\[ exp within the man page.


But it's not, as we can see by this example.

$ if [[ (3 < 15) ]]; then echo "OK"; fi $


Use Bash Integer Conditions

When writing conditional expressions that involve integer values, use the Bash ((...)) construct. Like [[...]], it uses C languagelike syntax. Although [[...]] is for general conditions, ((...)) operates only on integer values and variables.

Here's an example.

$ v1=3; v2=2 $ if (($v1 < $v2)); then echo "yes"; else echo "no"; fi no


You may omit the $ normally required for variable expansion.

$ if ((v1 < v2)); then echo "yes"; else echo "no"; fi no


Tip

To find out more about the ((...)) construct, check the Bash man page by typing

$ man bash


and then

/^ARITHMETIC EVALUATION within the man page.



The Bash ((...)) construct provides a more friendly syntax by employing && and ||, unescaped parentheses, and < in place of -lt. We could write

$ if (((a < b) && (b < c))); then...


or

$ while ((length!=0))





Mac OS X UNIX 101 Byte-Sized Projects
Mac OS X Unix 101 Byte-Sized Projects
ISBN: 0321374118
EAN: 2147483647
Year: 2003
Pages: 153
Authors: Adrian Mayo

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net