Bash – ny major difference when comparing a variable as a string or as an int

bashshelltest

Out of curiosity, when doing a bash variable comparison (its value being an integer) it's possible to test it against some predefined value either declared as an int or as a string.

Sample script:

#!/bin/bash
f1()
{
        [ "$1" == "1" ] && echo "$FUNCNAME: \"1\" compared as string"
}

f2()
{
        [[ "$1" -eq 1 ]] && echo "$FUNCNAME: \"1\" compared as int"
}

f1 $1
f2 $1

Output:

$  ./param.sh 1
f1: "1" compared as string
f2: "1" compared as int

and

$  ./param.sh blah
$

Both functions behave the same way, and so I'm wondering if there's a preferred way when checking an integer variable?
I would go for checking int versus int as it's more strict but I wonder if there are any draw backs doing it with string?

In this case, f2() is also more strict about the comparison, i.e. passing a decimal value will break it, whereas f1() will take it no problem.

Best Answer

Yep, lots of differences. For instance, = checks for exact string equality, but -eq evaluates both expressions arithmetically before checking for equality:

$ [ " 1 " -eq 1 ] && echo equal || echo not
equal
$ [ " 1 " = 1 ] && echo equal || echo not
not

$ [ +1 -eq 1 ] && echo equal || echo not
equal
$ [ +1 = 1 ] && echo equal || echo not
not

$ [ "0+1" -eq 1 ] && echo equal || echo not
equal
$ [ "0+1" = 1 ] && echo equal || echo not
not

Also, the empty string happens to be numerically equal to zero:

$ [ "" -eq 0 ] && echo equal || echo not
equal
$ [ "" = 0 ] && echo equal || echo not
not

And a whole other class of differences appears when you bring the comparison operators in - considering < vs -lt, for instance:

$ [[ 2 -lt 10 ]] && echo less || echo not
less
$ [[ 2 < 10 ]] && echo less || echo not
not

This is because the string "2" is alphabetically after the string "10" (since 1 comes before 2), but the number "2" is numerically less than the number "10".

Related Question