3

I know we should use = (or its alias ==) to compare strings in Bash. But I forgot about it and used -eq in a script, getting strange results, but no error.

I haven’t found any documentation regarding this but from my tests, [[ "$foo" -eq "$bar" ]] appears to always evaluate to 0, i.e. success, for any value of $foo or $bar, provided they don’t start with a digit:

$ [[ "1" -eq "1" ]] && echo yes || echo nope
yes
$ [[ "1" -eq "0" ]] && echo yes || echo nope
nope
$ [[ "x1" -eq "0" ]] && echo yes || echo nope
yes
$ [[ "x1" -eq "x0" ]] && echo yes || echo nope
yes
$ [[ foo -eq bar ]] && echo yes || echo nope
yes
$ [[ so -eq different ]] && echo yes || echo nope
yes

It does evaluate to 1 (error) when one value starts with a digit:

$ [[ 1x -eq 1 ]] && echo yes || echo nope
bash: [[: 1x : valeur trop grande pour la base (le symbole erroné est "1x")
nope

The error message I get is French for "value too high for the base (the erroneous symbol is "1x"".

Why doesn’t it return 1 and/or show an error when getting a non-number? It makes things really hard to debug when you made that mistake.

I used GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu).

bfontaine
  • 18,169
  • 13
  • 73
  • 107
  • The error is caused by the fact that anything starting with 1 is decimal, so can't have letters in it. – Mad Physicist Nov 24 '17 at 14:43
  • @MadPhysicist Yes, but my question is more about why there’s *no* error when comparing e.g. `foo` and `bar`. – bfontaine Nov 24 '17 at 14:47
  • 1
    As a first order guess, I'd assume this behavior means that strings that don't represent valid numbers get set to null or something like zero. Try comparing `"zzz" -le "kkk"` as well as `"zzz" -le "666"`. Using an operator other than `-eq` might be informative because a null clearly does not equal any number, but is neither greater nor less than. – Mad Physicist Nov 24 '17 at 14:47

1 Answers1

4

In arithmetic context bash would try first to see if it's a number and then would see if it's a valid var name. If it's a var name but the var is empty or not defined then the value will be 0.

[STEP 100] # echo $BASH_VERSION
4.4.12(1)-release
[STEP 101] # n=123
[STEP 102] # [[ n -eq 123 ]] && echo yes || echo no
yes
[STEP 103] # [[ 'n' -eq 123 ]] && echo yes || echo no
yes
[STEP 104] # varname=n
[STEP 105] # [[ $varname -eq 123 ]] && echo yes || echo no
yes
[STEP 106] # [[ notdefined -eq 123 ]] && echo yes || echo no
no
[STEP 107] # [[ notdefined -eq 0 ]] && echo yes || echo no
yes
[STEP 108] #

Even this works:

[STEP 108] # expr='100 + 20 + 3'
[STEP 109] # [[ expr -eq 123 ]] && echo yes || echo no
yes
[STEP 110] #

And the expansion is recursive:

[STEP 201] # a=b
[STEP 202] # b=c
[STEP 203] # c=123
[STEP 204] # [[ a -eq 123 ]] && echo yes || echo no
yes
[STEP 205] # (( a == 123 )) && echo yes || echo no
yes
[STEP 206] #

From bash's man page:

ARITHMETIC EVALUATION

Shell variables are allowed as operands; parameter expansion is performed before the expression is evaluated. Within an expression, shell variables may also be referenced by name without using the parameter expansion syntax. A shell variable that is null or unset evaluates to 0 when referenced by name without using the parameter expansion syntax. The value of a variable is evaluated as an arithmetic expression when it is referenced, or when a variable which has been given the integer attribute using declare -i is assigned a value. A null value evaluates to 0. A shell variable need not have its integer attribute turned on to be used in an expression.

pynexj
  • 19,215
  • 5
  • 38
  • 56