25

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.

1
  • Note that Bash doesn't really have an Integer datatype. You can basically just hint Bash to treat a String as an integer. Jul 11, 2014 at 9:04

3 Answers 3

22

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".

1
  • 2
    Don't forget there's also (( ... )) for numeric operations. (( " 1 " == 1 )) && echo yes || echo no results in yes
    – phemmer
    Jul 10, 2014 at 16:51
8

Integer vs. string comparison becomes most significant when you are comparing greater or lesser than:

#!/bin/bash

eleven=11
nine=9

[[ $nine < $eleven ]] && echo string   # fail

[[ "$nine" -lt "$eleven" ]] && echo integer # pass

The first one fails because 9 comes after 11 when sorted lexicographically.

Note that using quotes does not determine whether you are comparing strings or numbers, the operator does. You can add or remove the quotes above, it does not make any difference. Bash catches undefined variables within double brackets, so quotes are not necessary. Using quotes with single brackets for numerical tests won't save you since:

[ "" -lt 11 ]

is an error anyway ("integer expression required"). Quotes are an effective safeguard with string comparisons in single brackets:

[ "" \< 11 ]

Note within double brackets, "" will -eq 0 but not == 0.

2
  • 1
    In bash, it's not strictly necessary to quote variables within double brackets: the builtin [[ is smart enough to remember where the variables are, and it won't get fooled by empty variables. Single brackets ([) do not have this feature, and require quotes. Jul 10, 2014 at 15:28
  • @glennjackman Had not noticed that. [[ -lt 11 ]] is an error, but nothing=; [[ $nothing -lt 11 ]] is not. I've reworked the last paragraph a bit.
    – goldilocks
    Jul 10, 2014 at 15:48
3

In addition to what has been said.
Comparing for equality is faster with numbers, although in shell scripting it is rare that you need fast calculation.

$ b=234
$ time for ((a=1;a<1000000;a++)); do [[ $b = "234" ]]; done

real    0m13.008s
user    0m12.677s
sys 0m0.312s

$ time for ((a=1;a<1000000;a++)); do [[ $b -eq 234 ]]; done

real    0m10.266s
user    0m9.657s
sys 0m0.572s
5
  • Considering that they do different things, I'd say the performance is irrelevant - you need to use the one that does what you want.
    – godlygeek
    Jul 10, 2014 at 15:28
  • @godlygeek Equality comparison of a variable can be achieved both way. "-eq" is faster.
    – Emmanuel
    Jul 10, 2014 at 15:33
  • They test for different definitions of equality. If you want to answer the question "Does this variable hold the exact string 123", you can only use =, since using -eq would match for "+123" as well. If you wanted to know "Does this variable, when evaluated as an arithmetic expression, compare equal to 123", you could only use -eq. The only time I can see where a programmer wouldn't care which definition of equality was used is when he knows that the contents of the variable are constrained to a particular pattern ahead of time.
    – godlygeek
    Jul 10, 2014 at 16:16
  • @godlygeek interesting, the question was about comparing equality of numbers as if they were strings, does it fits the case of variables constrained ahead of time to a particular pattern ?
    – Emmanuel
    Jul 10, 2014 at 16:29
  • Your example (b=234) fits that pattern - you know it's not +234 or " 234 " or "233+1", since you assigned it yourself, so you know that it comparing it as a string and as a number are equally valid. But the OP's script, since it takes input as a command line argument, doesn't have that constraint - consider calling it as ./param.sh 0+1 or ./param.sh " 1"
    – godlygeek
    Jul 10, 2014 at 16:35

You must log in to answer this question.

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