44

In Bash, two integers can be compared using conditional expression

arg1 OP arg2

OP is one of -eq, -ne, -lt, -le, -gt, or -ge. These arithmetic binary operators return true if arg1 is equal to, not equal to, less than, less than or equal to, greater than, or greater than or equal to arg2, respectively. Arg1 and arg2 may be positive or negative integers.

or arithmetic expression:

<= >= < > comparison

== != equality and inequality

Why do we have two different ways for comparing two integers? When to use which?

For example, [[ 3 -lt 2 ]] uses conditional expression, and (( 3 < 2 )) uses arithmetic expression. Both returns 0 when the comparison is true

When comparing two integers, can these two methods always be used interchangeably? If yes, why does Bash have two methods rather than one?

ilkkachu
  • 138,973
Tim
  • 101,790

3 Answers3

54

Yes, we have two different ways of comparing two integers.

It seems that these facts are not widely accepted in this forum:

  1. Inside the idiom [ ] the operators for arithmetic comparison are -eq, -ne, -lt, -le, -gt and -ge.

    As they also are inside a test command and inside a [[ ]].

    Yes inside this idioms, =, <, etc. are string operators.

  2. Inside the idiom (( )) the operators for arithmetic comparison are ==, !=, <, <=, >, and >=.

    No, this is not an "Arithmetic expansion" (which start with a $) as $(( )). It is defined as a "Compound Command" in man bash.

    Yes, it follows the same rules (internally) of the "Arithmetic expansion" but has no output, only an exit value. It could be used like this:

if (( 2 > 1 )); then ...

Why do we have two different ways for comparing two integers?

I guess that the latter (( )) was developed as a simpler way to perform arithmetic tests. It is almost the same as the $(( )) but just has no output.

Why two? Well the same as why we have two printf (external and builtin) or four test (external test, builtin test, [ and [[). That's the way that the shells grow, improving some area in one year, improving some other the next year.

When to use which?

That's a very tough question because there should be no effective difference. Of course there are some differences in the way a [ ] work and a (( )) work internally, but: which is better to compare two integers? Any one!.

When comparing two integers, can these two methods always be used interchangeably?

For two numbers I am compelled to say yes.
But for variables, expansions, mathematical operations there may be key differences that should favor one or the other. I can not say that absolutely both are equal. For one, the (( )) could perform several math operations in sequence:

if (( a=1, b=2, c=a+b*b )); then echo "$c"; fi

If yes, why does Bash have two methods rather than one?

If both are helpful, why not?.

  • 1
    = is an assignment and == is a comparison in arithmetic expansions. The question quotes it correctly. But the answer is wrong. – ceving Aug 16 '18 at 16:22
  • 2
    Also ( is not a reserved word in bash so there is no need to put spaces around ((, as opposed to [ or [[ – Roland Feb 24 '20 at 09:54
  • ((2<3)) ; echo $? $((2<3)) -> 0 1 so not just the other has output. ...and drives me crazy ;) – gabor.zed Jul 27 '22 at 22:30
20

Historically, the test command existed first (at least as far back to Unix Seventh Edition in 1979). It used the operators = and != to compare strings, and -eq, -ne, -lt, etc. to compare numbers. For example, test 0 = 00 is false, but test 0 -eq 00 is true. I don't know why this syntax was chosen, but it may have been to avoid using < and >, which the shell would have parsed as redirection operators. The test command got another syntax a few years later: [ … ] is equivalent to test ….

The [[ … ]] conditional syntax, inside which < and > can be used as operators without quoting, was added later, in ksh. It kept backward compatibility with [ … ], so it used the same operators, but added < and > to compare strings (for example, [[ 9 > 10 ]] but [[ 9 -lt 10 ]]). For more information, see using single or double bracket - bash

Arithmetic expressions also came later than the test command, in the Korn shell, at some time in the 1980s. They followed the syntax of the C language, which was very popular in Unix circles. Thus they used C's operators: == for equality, <= for less-or-equal, etc.

Unix Seventh Edition didn't have arithmetic expressions, but it did have the expr command, which also implemented a C-like syntax for integer operations, including its comparison operators. In a shell script, the characters < and > had to be quoted to protect them from the shell, e.g. if expr 1 \< 2; … is equivalent to if test 1 -lt 2; …. The addition of arithmetic expressions to the shell made most uses of expr obsolete, so it isn't well-known today.

In an sh script, you'd generally use arithmetic expressions to calculate an integer value, and [ … ] to compare integers.

if [ "$((x + y))" -lt "$z" ]; then …

In a ksh, bash or zsh script, you can use ((…)) for both.

if ((x + y < z)); then …

The [[ … ]] form is useful if you want to use conditionals involving things other than integers.

2

According to the test man page, = and != are used for string comparisons while the expressions -eq, -gt, -lt, -ge, -le, and -ne are integer comparisons. I've always followed this convention when writing shell scripts and it always works. Be aware that if you have variables in the expression, you may need to quote the variables in some way to avoid doing a null comparison.

On paper, we do string/number comparisons without much thought. A computer on the other hand doesn't know if 987 is a number or a string of characters. You need the different operators to tell the computer what to do so you get the right result. There is some additional info here that explains some of the history. Essentially, the variables are untyped and have remained that way for historical compatibility.

Tim
  • 101,790
signal7
  • 71
  • In my post, =and != are arithmetic operators, while the manpage of test only shows conditional expression operators. – Tim Apr 24 '16 at 15:46