3

Could someone explain the following behavior of the "if" block of bash ?

I use the following simple code check if the first_value is less than the second_value

first_value=67
second_value=2

if [[  "${first_value}" < "${second_value}" ]];
then 
    echo "Yes"
else 
    echo "No"
fi

The issue is

If the second_value is 1,2,3,4,5,6,10,11,... the block will return "No"

But if the second_value is 7,8,9 the block will return "Yes" (must be "No")

The fix is to use "-lt" instead of "<" but I want to understand such behavior of the "if" block.

The bash version is "GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)"

The OS is "CentOS Linux release 7.5.1804 (Core)"

xenteros
  • 14,275
  • 12
  • 47
  • 81
Vic VKh
  • 123
  • 2
  • 9
  • 1
    @Inian : The question is not a duplicate. The "duplicate" link points to a discussion of how to implement numerical comparision, but the OP indicated that he at least knows one possibility how to do it correctly. Instead, he wanted to know **why** his wrong approach resulted in the behaviour he observed. – user1934428 Nov 14 '19 at 10:08
  • @Inian : Thank you. I have deleted my comment and created an answer instead. – user1934428 Nov 14 '19 at 10:23
  • Hi Vic, did my answer solve your problem? [What should I do when someone answers my question](https://stackoverflow.com/help/someone-answers) – xenteros Nov 14 '19 at 11:41
  • 2
    This isn't about the `if` block; it's about the `[[ ... ]]` command, which is *not* part of the `if` block's syntax. – chepner Nov 14 '19 at 12:20
  • 1
    bash can provide documentation right from the prompt: enter `help [[` then `help test` – glenn jackman Nov 14 '19 at 15:24

2 Answers2

4

The reason for this behavior is the fact, that the [[ "${first_value}" < "${second_value}" ]] is actually lexicographic comparison, because of [[ exp ]] brackets.

As the OP mentions in the question, the possible fix is to use -lt operator.

In bash, you can use arithmetic context:

if (( $first_value < $second_value ));
then 
    echo "Yes"
else 
    echo "No"
fi

or even simpler

if (( first_value < second_value ));
then 
    echo "Yes"
else 
    echo "No"
fi
xenteros
  • 14,275
  • 12
  • 47
  • 81
  • Actually, explicit dereferencing of the variables in not necessary inside `(( ... ))`, so the statement can be written as `if (( first_value < second_value ))`, which I would consider a (tiny) bit more readable. – user1934428 Nov 14 '19 at 10:25
1

As for your question: First, an if block does not return anything. It just executes one set of statements or another ones, which in your case write something to stdout.

Now to the explanation of the behaviour you have observed.

The < operator in a [[ ... ]] expression does lexicographic comparison. For instance in

[[ 67 < 7 ]]

"67" is lexically smaller than "7", and hence [[ .... ]] returns status code 0, which means that the if is executing the then part, which causes Yes to be printed.

marc_s
  • 675,133
  • 158
  • 1,253
  • 1,388
user1934428
  • 12,137
  • 4
  • 27
  • 62