13

I was writing a bash script and suddenly this behaviour started:

[[ 1 < 2 ]]; echo $?  # outputs 0
[[ 2 < 13 ]]; echo $? # outputs 1

but -lt works soundly:

[[ 1 -lt 2 ]]; echo $?  # outputs 0
[[ 2 -lt 13 ]]; echo $? # outputs 0

did I accidentally overwrite < somehow?

here is a script I wrote to test this behaviour:

#!/bin/bash

for a in {1..5}
do
    for b in {1..20}
    do
        [[ $a < $b ]] && echo $a $b
    done

    echo
done

here is the output:

1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
1 11
1 12
1 13
1 14
1 15
1 16
1 17
1 18
1 19
1 20

2 3
2 4
2 5
2 6
2 7
2 8
2 9
2 20

3 4
3 5
3 6
3 7
3 8
3 9

4 5
4 6
4 7
4 8
4 9

5 6
5 7
5 8
5 9

changing < to -lt in the script gives normal output (5 10 shows up for example).

Rebooting did not change anything.

My bash version is GNU bash, version 4.3.42(1)-release (x86_64-pc-linux-gnu). I am on Ubuntu 15.10. I don't know what other information is relevant here.

1
  • 16
    with < operation, you are making a string comparison, whereas -lt operator is numeric comparison, If you look at the results you have listed, you will realize it. Numerically 2 is less than 10, alphabetically, the other way around.
    – MelBurslan
    Commented May 31, 2016 at 18:50

3 Answers 3

52

From the bash man page.

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

From the output, it appears to be working as designed.

In bash use:

  • [[ $a < $b ]] for lexicographical comparison as per the current locale (uses strcoll(3))
  • [ "$a" -lt "$b" ] for decimal integer comparison.
  • (( a < b )) or [[ $a -lt $b ]] for arithmetic expression comparisons. Beware that as the arithmetic expressions stored in $a and $b are being evaluated, that can have side effects including running arbitrary commands. For instance, x=5 a='++x' bash -c '[[ $a -lt $a ]]; echo "$? $x"' outputs 0 7 and a='x[$(reboot)]' b=a bash -c '[[ $a -lt $b ]]' reboots the system (twice). Also note that for those, numbers with leading 0 are interpreted as octal, not decimal.
4
  • 36
    In other words, read the man page before you assume you have found a bug. ;)
    – Wildcard
    Commented May 31, 2016 at 19:10
  • True. Perhaps the script i originally was working on started actually working 'correctly' (as in failing [[ $myvar < 13 ]]) when i noticed this behavior then. Is there anything I should do to this question now? Do we on this site change the title to include [solved] or something? Should the title be changed anyway to something more descriptive? Commented May 31, 2016 at 19:18
  • 2
    No, don't change the title of the question. The check mark next to this answer is enough to indicate the question has been solved. Commented May 31, 2016 at 20:22
  • 15
    @Wildcard The OP doesn't assume they found a bug. They explicitly suggest that perhaps they have done something to alter the behavior. Even the title assumes as much!
    – jpmc26
    Commented Jun 1, 2016 at 1:04
5

How about:

for a in {1..5}; 
do     
  for b in {1..20};     
  do         
    (( $a < $b )) && echo $a $b
  done      
  echo
done

According to http://www.tldp.org/LDP/abs/html/dblparens.html

Similar to the let command, the (( ... )) construct permits arithmetic expansion and evaluation. In its simplest form, a=$(( 5 + 3 )) would set a to 5 + 3, or 8. However, this double-parentheses construct is also a mechanism for allowing C-style manipulation of variables in Bash, for example, (( var++ )).

2
  • 2
    You are missing the point of the question - it is not "how can I compare those values?" but "why does it behave the way it does?".
    – guntbert
    Commented May 31, 2016 at 20:09
  • 7
    This was already answered. But as he wanted to use an arithmetic expression it might be worth pointing out that there exist constructs that allow you to use those in shell. Commented May 31, 2016 at 20:17
3

Firstly, [[ is not POSIX and should be avoided.

Secondly, if you wish to use < as part of an arithmetic test you can do this, but with different syntax:

if [ "$((2 < 13))" -ne 0 ]
then
  echo '2 is less than 13'
else
  echo '2 is greater or equal to 13'
fi

Or:

if expr 2 '<' 13
then
  echo '2 is less than 13'
else
  echo '2 is greater or equal to 13'
fi

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .