Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shell: Does "a -lt b" not mean true if a is less than b?

Tags:

linux

shell

I'm not trained in Linux, but I can muddle through with some doc lookups, but I'm stumped.

I found a script that helps set the date on my dd wrt router when it starts up, but only if the current date is less than the stored date. I can share the whole script if you want, but it boils down to this statement not evaluating to true when I expect it to. I'm putting the literals in, rather than the variables, and it still does not return true, it executes the "else" statement:

 if [ 021715402012 -lt 021815402012 ]
  then
     echo "the first seems less than the second"
  else
     echo "the first does not seem less than the second for some reason"
  fi

I would expect "the first seems less than the second" but this is not the case... Is it an overflow problem? I tried to make it a string compare like this:

if [ x021715402012 -lt x021815402012 ]

and tried putting it in quotes:

if [ "x021715402012" -lt "x021815402012" ]

it always executes the else. Does "a -lt b" not mean true if a is less than b?

Any insight into this would be appreciated, I'm stumped!

like image 868
Brian B Avatar asked Feb 19 '12 00:02

Brian B


1 Answers

The mnemonics such as -lt might be said to come from the original Fortran comparators such as .LT. from the late 1950s.

Yes, in the shell, -lt does a 'less than' numeric comparison. (Be aware, though, that in Perl, the numeric comparisons are < etc, and the string comparisons are denoted by the alphabetic operators such as -lt!)

However, in some, perhaps many, shells the conversion and comparison may well be done in the local long integer format. If you're on a 32-bit machine, the values you quote exceed the 32-bit (signed) range by a factor of 10 or so. On a 64-bit machine, or with a shell that uses long long, you'd be OK.

The hex equivalents of the decimal numbers are 021715402012 = 0x50E56BD1C and 021815402012 = 0x5144C9E1C; they can't be octal because of the 8. (However, if the shell does interpret the leading zero as 'octal', then the second number is just 021 or 17 decimal because the 8 ends the octal number. However, the 64-bit shells I tested on (Mac OS X 10.7.3 and RHEL 5) both seemed to treat them as decimal, not octal.)

The example code below, compiled under 64-bit gives the following output:

021715402012 =  240565532 = 0x050E56BD1C
021815402012 =  340565532 = 0x05144C9E1C

Compiled under 32-bit, it gives the following output:

021715402012 = 2147483647 = 0x007FFFFFFF
021815402012 = 2147483647 = 0x007FFFFFFF

If this was what happened in your shell, then the resulting behaviour of -lt would be explained. You could confirm it by testing whether the two values are -eq; counter-intuitively, this would probably evaluate to true under the hypothesis that you are using a 32-bit shell that limits its arithmetic to long (32-bit) signed integers.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char *array[] = { "021715402012", "021815402012" };
    for (int i = 0; i < 2; i++)
    {
        int j = atoi(array[i]);
        long k = strtol(array[i], 0, 10);
        printf("%-10s = %10d = 0x%.10lX\n", array[i], j, k);
    }
    return 0;
}
like image 57
Jonathan Leffler Avatar answered Oct 08 '22 00:10

Jonathan Leffler