Is there any major difference when comparing a variable as a string or as an int

18,167

Solution 1

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

Solution 2

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.

Solution 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
Share:
18,167

Related videos on Youtube

user1845506
Author by

user1845506

Updated on September 18, 2022

Comments

  • user1845506
    user1845506 almost 2 years

    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.

  • godlygeek
    godlygeek almost 10 years
    Considering that they do different things, I'd say the performance is irrelevant - you need to use the one that does what you want.
  • Angel Todorov
    Angel Todorov almost 10 years
    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.
  • Emmanuel
    Emmanuel almost 10 years
    @godlygeek Equality comparison of a variable can be achieved both way. "-eq" is faster.
  • goldilocks
    goldilocks almost 10 years
    @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.
  • godlygeek
    godlygeek almost 10 years
    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.
  • Emmanuel
    Emmanuel almost 10 years
    @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 ?
  • godlygeek
    godlygeek almost 10 years
    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"
  • phemmer
    phemmer almost 10 years
    Don't forget there's also (( ... )) for numeric operations. (( " 1 " == 1 )) && echo yes || echo no results in yes