Differences between revisions 2 and 10 (spanning 8 versions)
Revision 2 as of 2008-11-22 14:09:45
Size: 1521
Editor: localhost
Comment: converted to 1.6 markup
Revision 10 as of 2022-10-20 20:40:35
Size: 2489
Editor: emanuele6
Comment: since e.g. [[ !($var) ]] is parsed differently based on the state of extglob (off: [[ -n "!($var)" ]]; on: [[ ! ( -n $var ) ]]). avoid using [[ with optional whitespace omitted to prevent ambiguities.
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
== How can I group expressions, e.g. (A AND B) OR C? ==
The TestCommand {{{[}}} uses parentheses () for expression grouping. Given that "AND" is "-a", and "OR" is "-o", the following expression
== How can I group expressions in an if statement, e.g. if (A AND B) OR C? ==
The portable (POSIX or Bourne) way is to use multiple `test` (or `[`) commands:
Line 5: Line 5:
{{{
    (0<n AND n<=10) OR n=-1
{{{#!highlight bash
# Bourne
if commandA && commandB || commandC; then
...

# or with test(1) calls:
if [ testA ] && [ testB ] || [ testC ]; then
...
Line 9: Line 16:
can be written as follows: When they are shell operators between commands (as opposed to the `[[...]]` operators), `&&` and `||` have equal precedence, so processing is left to right.
Line 11: Line 18:
{{{
    if [ \( $n -gt 0 -a $n -le 10 \) -o $n -eq -1 ]
    then
        echo "0 < $n <= 10, or $n=-1"
    else
        echo "invalid number: $n"
    fi
If we need explicit grouping, then we can use curly braces:

{{{#!highlight bash
# Bourne
if commandA && { commandB || commandC; }; then
...
Line 20: Line 26:
Note that the parentheses have to be quoted: \(, '(' or "(". What we should ''not'' do is try to use the `-a` or `-o` operators of the `test` command, because the results are undefined.
Line 22: Line 28:
[[BASH]] and KornShell have different, more powerful comparison commands with slightly different (easier) quoting: [[BASH]], zsh and the KornShell have different, more powerful comparison commands with slightly different (easier) quoting:
Line 27: Line 33:
{{{
    if (( (n>0 && n<10) || n == -1 ))
    then echo "0 < $n < 10, or n==-1"
    fi
{{{#!highlight bash
# Bash/ksh/zsh
if (( (n>0 && n<10) || n == -1 ))
then echo "0 < $n < 10, or n==-1"
fi
Line 34: Line 41:
{{{
    if [[ ( -f $localconfig && -f $globalconfig ) || -n $noconfig ]]
    then echo "configuration ok (or not used)"
    fi
{{{#!highlight bash
# Bash/ksh/zsh
if [[ ( -f $localconfig && -f $globalconfig ) || -n $noconfig ]]
then echo "configuration ok (or not used)"
fi
Line 40: Line 48:
Note that the distinction between numeric and string comparisons is strict. Consider the following example:
{{{
    n=3
    if [[ n>0 && n<10 ]]
    then echo "$n is between 0 and 10"
    else echo "ERROR: invalid number: $n"
    fi
Note that contrary to the `&&` and `||` ''shell'' operators, the `&&` operator in `((...))` and `[[...]]` has precedence over the `||` operator (same goes for `[`'s `-a` over `-o`), so for instance:

{{{#!highlight bash
[ a = a ] || [ b = c ] && [ c = d ]
Line 49: Line 54:
The output will be "ERROR: ....", because in a ''string comparision'' "3" is bigger than "10", because "3" already comes after "1", and the next character "0" is not considered. Changing the square brackets to double parentheses {{{((}}} makes the example work as expected. is '''false''' because it's like:

{{{#!highlight bash
{ [ a = a ] || [ b = c ]; } && [ c = d ]
}}}

(left to right association, no precedence), while

{{{#!highlight bash
[[ a = a || b = c && c = d ]]
}}}

is '''true''' because it's like:

{{{#!highlight bash
[[ a = a || ( b = c && c = d ) ]]
}}}

(`&&` has precedence over `||`).

Note that the distinction between numeric and string comparisons is strict. Consider the following example:
{{{#!highlight bash
n=3
if [[ $n > 0 && $n < 10 ]]
then echo "$n is between 0 and 10"
else echo "ERROR: invalid number: $n"
fi
}}}

The output will be "ERROR: ....", because in a ''string comparison'' "3" is bigger than "10", because "3" already comes after "1", and the next character "0" is not considered. Changing the square brackets to double parentheses {{{(( ))}}} makes the example work as expected.

----
CategoryShell

How can I group expressions in an if statement, e.g. if (A AND B) OR C?

The portable (POSIX or Bourne) way is to use multiple test (or [) commands:

   1 # Bourne
   2 if commandA && commandB || commandC; then
   3 ...
   4 
   5 # or with test(1) calls:
   6 if [ testA ] && [ testB ] || [ testC ]; then
   7 ...

When they are shell operators between commands (as opposed to the [[...]] operators), && and || have equal precedence, so processing is left to right.

If we need explicit grouping, then we can use curly braces:

   1 # Bourne
   2 if commandA && { commandB || commandC; }; then
   3 ...

What we should not do is try to use the -a or -o operators of the test command, because the results are undefined.

BASH, zsh and the KornShell have different, more powerful comparison commands with slightly different (easier) quoting:

Examples:

   1 # Bash/ksh/zsh
   2 if (( (n>0 && n<10) || n == -1 ))
   3 then echo "0 < $n < 10, or n==-1"
   4 fi

or

   1 # Bash/ksh/zsh
   2 if [[ ( -f $localconfig && -f $globalconfig ) || -n $noconfig ]]
   3 then echo "configuration ok (or not used)"
   4 fi

Note that contrary to the && and || shell operators, the && operator in ((...)) and [[...]] has precedence over the || operator (same goes for ['s -a over -o), so for instance:

   1 [ a = a ] || [ b = c ] && [ c = d ]

is false because it's like:

   1 { [ a = a ] || [ b = c ]; } && [ c = d ]

(left to right association, no precedence), while

   1 [[ a = a || b = c && c = d ]]

is true because it's like:

   1 [[ a = a || ( b = c && c = d ) ]]

(&& has precedence over ||).

Note that the distinction between numeric and string comparisons is strict. Consider the following example:

   1 n=3
   2 if [[ $n > 0 && $n < 10 ]]
   3 then echo "$n is between 0 and 10"
   4 else echo "ERROR: invalid number: $n"
   5 fi

The output will be "ERROR: ....", because in a string comparison "3" is bigger than "10", because "3" already comes after "1", and the next character "0" is not considered. Changing the square brackets to double parentheses (( )) makes the example work as expected.


CategoryShell

BashFAQ/017 (last edited 2022-10-20 20:40:35 by emanuele6)