Expressions


An expression is composed of constants, variables, and operators that can be processed to return a value. This section covers arithmetic, logical, and conditional expressions as well as operators. Table 28-8 on page 943 lists the bash operators.

Arithmetic Evaluation

The Bourne Again Shell can perform arithmetic assignments and evaluate many different types of arithmetic expressions, all using integers. The shell performs arithmetic assignments in a number of ways. One is with arguments to the let builtin:

$ let "VALUE=VALUE * 10 + NEW"


In the preceding example, the variables VALUE and NEW contain integer values. Within a let statement you do not need to use dollar signs ($) in front of variable names. Double quotation marks must enclose a single argument, or expression, that contains SPACEs. Because most expressions contain SPACEs and need to be quoted, bash accepts ((expression)) as a synonym for let "expression", obviating the need for both quotation marks and dollar signs:

$ ((VALUE=VALUE * 10 + NEW))


You can use either form wherever a command is allowed and can remove the SPACEs if you like. In the following example, the asterisk (*) does not need to be quoted because the shell does not perform pathname expansion on the right side of an assignment (page 288):

$ let VALUE=VALUE*10+NEW


Because each argument to let is evaluated as a separate expression, you can assign values to more than one variable on a single line:

$ let "COUNT = COUNT + 1" VALUE=VALUE*10+NEW


You need to use commas to separate multiple assignments within a set of double parentheses:

$ ((COUNT = COUNT + 1, VALUE=VALUE*10+NEW))


Tip: Arithmetic evaluation versus arithmetic expansion

Arithmetic evaluation differs from arithmetic expansion. As explained on page 332, arithmetic expansion uses the syntax $((expression)), evaluates expression, and replaces $((expression)) with the result. You can use arithmetic expansion to display the value of an expression or to assign that value to a variable.

Arithmetic evaluation uses the let expression or ((expression)) syntax, evaluates expression, and returns a status code. You can use arithmetic evaluation to perform a logical comparison or an assignment.


Logical expressions


You can use the ((expression)) syntax for logical expressions, although that task is frequently left to [[expression]]. The next example expands the age_check script (page 332) to include logical arithmetic evaluation in addition to arithmetic expansion:

$ cat age2 #!/bin/bash echo -n "How old are you? " read age if ((30 < age && age < 60)); then         echo "Wow, in $((60-age)) years, you'll be 60!"     else         echo "You are too young or too old to play." fi $ age2 How old are you? 25 You are too young or too old to play.


The test-statement for the if structure evaluates two logical comparisons joined by a Boolean AND and returns 0 (true) if they are both true or 1 (false) otherwise.

Logical Evaluation (Conditional Expressions)

The syntax of a conditional expression is


[[ expression ]]

where expression is a Boolean (logical) expression. You must precede a variable name with a dollar sign ($) within expression. The result of executing this builtin, like the test builtin, is a return status. The conditions allowed within the brackets are almost a superset of those accepted by test (page 879). Where the test builtin uses a as a Boolean AND operator, [[ expression]] uses &&. Similarly, where test uses o as a Boolean OR operator, [[ expression ]] uses ||.

You can replace the line that tests age in the age2 script (preceding) with the following conditional expression. You must surround the [[ and ]] tokens with whitespace or a command terminator, and place dollar signs before the variables:

if [[ 30 < $age && $age < 60 ]]; then


You can also use test's relational operators gt, ge, lt, le, eq, and ne:

if [[ 30 -lt $age && $age -lt 60 ]]; then


String comparisons


The test builtin tests whether strings are equal or unequal. The [[expression]] syntax adds comparison tests for string operators. The > and < operators compare strings for order (for example, "aa" < "bbb"). The = operator tests for pattern match, not just equality: [[ string= pattern]] is true if string matches pattern. This operator is not symmetrical; the pattern must appear on the right side of the equal sign. For example, [[ artist = a* ]] is true (= 0), whereas [[ a* = artist ]] is false (= 1):

$ [[ artist = a*  ]] $ echo $? 0 $ [[ a*  = artist ]] $ echo $? 1


The next example uses a command list that starts with a compound condition. The condition tests that the directory bin and the file src/myscript.bash exist. If this is true, cp copies src/myscript.bash to bin/myscript. If the copy succeeds, chmod makes myscript executable. If any of these steps fails, echo displays a message.

$ [[ -d bin && -f src/myscript.bash ]] && cp src/myscript.bash \ bin/myscript && chmod +x bin/myscript || echo "Cannot make \ executable version of myscript"


String Pattern Matching

The Bourne Again Shell provides string pattern-matching operators that can manipulate pathnames and other strings. These operators can delete from strings prefixes or suffixes that match patterns. The four operators are listed in Table 28-7.

Table 28-7. String operators

Operator

Function

#

Removes minimal matching prefixes

##

Removes maximal matching prefixes

%

Removes minimal matching suffixes

%%

Removes maximal matching suffixes


The syntax for these operators is


${varname op pattern}

where op is one of the operators listed in Table 28-7 and pattern is a match pattern similar to that used for filename generation. These operators are commonly used to manipulate pathnames so as to extract or remove components or to change suffixes:

$ SOURCEFILE=/usr/local/src/prog.c $ echo ${SOURCEFILE#/* /} local/src/prog.c $ echo ${SOURCEFILE##/*/} prog.c $ echo ${SOURCEFILE%/*} /usr/local/src $ echo ${SOURCEFILE%%/*} $ echo ${SOURCEFILE%.c} /usr/local/src/prog $ CHOPFIRST=${SOURCEFILE#/*/} $ echo $CHOPFIRST local/src/prog.c $ NEXT=${CHOPFIRST%%/*} $ echo $NEXT local


Here the string-length operator, ${#name}, is replaced by the number of characters in the value of name:

$ echo $SOURCEFILE /usr/local/src/prog.c $ echo ${#SOURCEFILE} 21


Operators

Arithmetic expansion and arithmetic evaluation use the same syntax, precedence, and associativity of expressions as the C language. Table 28-8 lists operators in order of decreasing precedence (priority of evaluation); each group of operators has equal precedence. Within an expression you can use parentheses to change the order of evaluation.

Table 28-8. Operators

Type of operator/operator

Function

Post

 
 

var++ Postincrement

var Postdecrement

Pre

 
 

++var Preincrement

var Predecrement

Unary

 
 

Unary minus

 

+ Unary plus

Negation

 
 

! Boolean NOT (logical negation)

 

~ Complement (bitwise negation)

Exponentiation

 
 

** Exponent

Multiplication, division, remainder

 
 

* Multiplication

 

/ Division

 

% Remainder

Addition, subtraction

 
 

Subtraction

 

+ Addition

Bitwise shifts

 
 

<< Left bitwise shift

 

>> Right bitwise shift

Comparison

 
 

<= Less than or equal

 

>= Greater than or equal

 

< Less than

 

> Greater than

Equality, inequality

 
 

== Equality

 

!= Inequality

Bitwise

 
 

& Bitwise AND

 

^ Bitwise XOR (exclusive OR)

 

| Bitwise OR

Boolean (logical)

 
 

&& Boolean AND

 

|| Boolean OR

Conditional evaluation

 
 

? : Ternary operator

Assignment

 

=, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |= Assignment

Comma

 
 

, Comma


Pipe


The pipe token has higher precedence than operators. You can use pipes anywhere in a command that you can use simple commands. For example, the command line

$ cmd1 | cmd2 || cmd3 | cmd4 && cmd5 | cmd6


is interpreted as if you had typed

$ ((cmd1 | cmd2) || (cmd3 | cmd4)) && (cmd5 | cmd6)


Tip: Do not rely on rules of precedence: use parentheses

Do not rely on the precedence rules when you use compound commands. Instead, use parentheses to explicitly state the order in which you want the shell to interpret the commands.


Increment and decrement operators


The postincrement, postdecrement, preincrement, and predecrement operators work with variables. The pre- operators, which appear in front of the variable name as in ++COUNT and VALUE, first change the value of the variable (++ adds 1; subtracts 1) and then provide the result for use in the expression. The post- operators appear after the variable name as in COUNT++ and VALUE; they first provide the unchanged value of the variable for use in the expression and then change the value of the variable.

$ N=10 $ echo $N 10 $ echo $((--N+3)) 12 $ echo $N 9 $ echo $((N++ - 3)) 6 $ echo $N 10


Remainder


The remainder operator (%) gives the remainder when its first operand is divided by its second. For example, the expression $((15%7)) has the value 1.

Boolean


The result of a Boolean operation is either 0 (false) or 1 (true).

The && (AND) and || (OR) Boolean operators are called short-circuiting operators. If the result of using one of these operators can be decided by looking only at the left operand, the right operand is not evaluated. The && operator causes the shell to test the exit status of the command preceding it. If the command succeeded, bash executes the next command; otherwise, it skips the remaining commands on the command line. You can use this construct to execute commands conditionally:

$ mkdir bkup && cp -r src bkup


This compound command creates the directory bkup. If mkdir succeeds, the contents of directory src is copied recursively to bkup.

The || separator also causes bash to test the exit status of the first command but has the opposite effect: The remaining command(s) are executed only if the first one failed (that is, exited with nonzero status):

$ mkdir bkup || echo "mkdir of bkup failed" >> /tmp/log


The exit status of a command list is the exit status of the last command in the list. You can group lists with parentheses. For example, you could combine the previous two examples as

$ (mkdir bkup && cp -r src bkup) || echo "mkdir failed" >> /tmp/log


In the absence of parentheses, && and || have equal precedence and are grouped from left to right. The following examples use the true and false utilities. These utilities do nothing and return true (0) and false (1) exit statuses, respectively:

$ false; echo $? 1


The $? variable holds the exit status of the preceding command (page 920). The next two commands yield an exit status of 1 (false):

$ true || false && false $ echo $? 1 $ (true || false) && false $ echo $? 1


Similarly the next two commands yield an exit status of 0 (true):

$ false && false || true $  echo $? 0 $ (false && false) || true $ echo $? 0


Because || and && have equal precedence, the parentheses in the two preceding pairs of examples do nothing to change the order of operations.

Because the expression on the right side of a short-circuiting operator may never get executed, you must be careful with assignment statements in that location. The following example demonstrates what can happen:

$ ((N=10,Z=0)) $ echo $((N || ((Z+=1)) )) 1 $ echo $Z 0


Because the value of N is nonzero, the result of the || (OR) operation is 1 (true), no matter what the value of the right side is. As a consequence ((Z+=1)) is never evaluated and Z is not incremented.

Ternary


The ternary operator, ? :, decides which of two expressions should be evaluated, based on the value returned from a third expression:

expression1 ? expression2 : expression3


If expression1 produces a false (0) value, expression3 is evaluated; otherwise, expression2 is evaluated. The value of the entire expression is the value of expression2 or expression3, depending on which one is evaluated. If expression1 is true, expression3 is not evaluated. If expression1 is false expression2 is not evaluated:

$ ((N=10,Z=0,COUNT=1)) $ ((T=N>COUNT?++Z:--Z)) $ echo $T 1 $ echo $Z 1


Assignment


The assignment operators, such as +=, are shorthand notations. For example, N+=3 is the same as ((N=N+3)).

Other bases


The following commands use the syntax base#n to assign base 2 (binary) values. First v1 is assigned a value of 0101 (5 decimal) and v2 is assigned a value of 0110 (6 decimal). The echo utility verifies the decimal values.

$ ((v1=2#0101)) $ ((v2=2#0110)) $ echo "$v1 and $v2" 5 and 6


Next the bitwise AND operator (&) selects the bits that are on in both 5 (0101 binary) and 6 (0110 binary). The result is binary 0100, which is 4 decimal.

$ echo $(( v1 & v2 )) 4


The Boolean AND operator (&&) produces a result of 1 if both of its operands are nonzero and a result of 0 otherwise. The bitwise inclusive OR operator (|) selects the bits that are on in either 0101 or 0110, resulting in 0111, which is 7 decimal. The Boolean OR operator (||) produces a result of 1 if either of its operands is nonzero and a result of 0 otherwise.

$  echo $(( v1 && v2 )) 1 $  echo $(( v1 | v2 )) 7 $  echo $(( v1 || v2 )) 1


Next the bitwise exclusive OR operator (^) selects the bits that are on in either, but not both, of the operands 0101 and 0110, yielding 0011, which is 3 decimal. The Boolean NOT operator (!) produces a result of 1 if its operand is 0 and a result of 0 otherwise. Because the exclamation point in $(( ! v1 )) is enclosed within double parentheses, it does not need to be escaped to prevent the shell from interpreting the exclamation point as a history event. The comparison operators produce a result of 1 if the comparison is true and a result of 0 otherwise.

$  echo $(( v1 ^ v2 )) 3 $  echo $(( ! v1 )) 0 $  echo $(( v1 < v2 )) 1 $  echo $(( v1 < v2 )) 0





A Practical Guide to Red Hat Linux
A Practical Guide to Red HatВ® LinuxВ®: Fedoraв„ў Core and Red Hat Enterprise Linux (3rd Edition)
ISBN: 0132280272
EAN: 2147483647
Year: 2006
Pages: 383

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