1
# prints "here" when run in bash
if [[ ((9 > 220)) ]]; then echo "here"; fi

I'm confused why the above if statement gets evaluated to true. Wouldn't ((9 > 220)) evaluate to false which would make the if statement false? The code below behaves as expected. I'm confused why using the double parentheses in a double brackets "if" isn't working above though.

# doesn't print anything
if ((9 > 220)); then echo "here"; fi
Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
Daniel E
  • 394
  • 3
  • 13

4 Answers4

1

It is because using double brackets makes it a lexicographical comparison. That means that it checks for which sequence is longer, like sorting alphabetically. More information about lexicographical comparisons here

Cary Shindell
  • 1,336
  • 8
  • 25
  • So the double hard brackets change the behavior of the nested double parentheses (which would normally use the arithmetic context)? – Daniel E Jul 21 '17 at 13:34
  • Or actually I see that if [[ 0 ]] evaluates to true so I'm guessing the arithmetic context is still behaving as expected but it's the double hard brackets that are making if [[ 0 ]] evaluate to true. – Daniel E Jul 21 '17 at 13:37
  • That is the case, yes. Bash expressions are evaluated differently. Interesting indeed – Cary Shindell Jul 21 '17 at 13:49
  • 2
    And to expand on this answer, because we are inside double brackets, the parentheses are just for grouping conditional expressions -- they do not introduce an arithmetic context at all. See https://www.gnu.org/software/bash/manual/bashref.html#Conditional-Constructs, scroll down for the `[[ ... ]]` part, then scroll down further for `( expression )` – glenn jackman Jul 21 '17 at 14:21
1

There's a fundamental difference between those two compound commands inside a conditional construct.

[[ expression ]] compound command

In your first example you're using the [[ expression ]] command:

if [[ ((9 > 220)) ]]; then echo "here"; fi

where parenthesis are treated merely as grouping operators, used to override the normal precedence of other operators (like !, &&, ||, >, -gt, -e, etc.). The > operator in this case is a lexicographic greater than.

This is nicely described in man bash:

[[ expression ]]

Return a status of 0 or 1 depending on the evaluation of the conditional expression expression. Expressions are composed of the primaries described below under CONDITIONAL EXPRESSIONS. Word splitting and pathname expansion are not performed on the words between the [[ and ]]; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed. Conditional operators such as -f must be unquoted to be recognized as primaries.

When used with [[, the < and > operators sort lexicographically using the current locale.

So, to compare integers in the [[ compound command, you can use the conditional expression operators, the same one used by test and [ commands. For example like this:

if [[ 9 -gt 220 ]]; then echo "here"; fi

The result is the same like when the -gt operator is grouped with parenthesis:

if [[ ((9 -gt 220)) ]]; then echo "here"; fi

Alternatively, you can use the arithmetic expansion and exploit the fact that the boolean results are represented as "0" or "1":

if [[ $((9 > 200)) == 1 ]]; then echo "here"; fi

(( expression )) compound command

In your second example, you're using the (( expression )) command:

if ((9 > 220)); then echo "here"; fi

where the expression is evaluated according to the rules of the shell arithmetic. The man bash says:

((expression))

The expression is evaluated according to the rules described below under ARITHMETIC EVALUATION. If the value of the expression is non-zero, the return status is 0; otherwise the return status is 1. This is exactly equivalent to let "expression".

randomir
  • 17,989
  • 1
  • 40
  • 55
0

Inside of [[ ]], the parentheses don't trigger an arithmetic context, they're interpreted purely for grouping/precedence1. All of these are equivalent:

$ [[ 0 < 1 ]] && echo yes
yes
$ [[ (0 < 1) ]] && echo yes
yes
$ [[ ((0 < 1)) ]] && echo yes
yes
$ [[ (((0 < 1))) ]] && echo yes
yes
$ [[ ((((0 < 1)))) ]] && echo yes
yes

If we have unbalanced parentheses, Bash complains about that:

$ [[ ((((0 < 1))) ]] && echo yes
bash: unexpected token `]]', expected `)'
bash: syntax error near `]]'

This all being said, < and > within [[ ]] are for lexicographical string comparison, so the statement above just checks if 0 is lexicographically sorted before 1 (it is).

Observe:

$ [[ 11 < 2 ]] && echo yes
yes

To compare numbers, you have to use -gt, -lt, -ge, -le, eq, ne instead of >, <, >=, <=, =, !=:

$ [[ 11 -lt 2 ]] && echo yes || echo no
no

Since you're already using (( )), you could use just that for comparing numbers:

$ (( 11 < 2 )) && echo yes || echo no
no

This is the simplest and clearest method in my opinion, if you know you have Bash at your disposal.


1 See the manual about [[ ]]:

Expressions may be combined using the following operators, listed in decreasing order of precedence:

( expression )
Returns the value of expression. This may be used to override the normal precedence of operators.

Hat tip to glenn jackman for pointing to the manual in his comment to another answer.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116
  • Thank you! I think I'm going to start just using if (( )); for all my if statements instead of double hard brackets. I like that the > and < symbols actually compare variables as integers and that you can also do && and || within the arithmetic context. – Daniel E Jul 21 '17 at 17:58
-1

The double parentheses is an arithmetic context. If you use the brackets, you have to use -lt for less than and -gt for greater than. See Comparing numbers in Bash.

Elan Hamburger
  • 2,137
  • 10
  • 14
  • Are you not supposed to use double parentheses within double hard brackets in bash? Shouldn't the ((9 > 220) evaluate first? I know you're allowed to use the > symbol to compare integers in the arithmetic context. – Daniel E Jul 21 '17 at 13:32
  • No. You use double parentheses or double brackets, not both. – Elan Hamburger Jul 21 '17 at 13:48