String comparison with integer in [[ test

One difference between [ and [[ is that [ does not do arithmetic evaluation but [[ does:

$ [ "2 + 2" -eq 4 ] && echo yes
bash: [: 2 + 2: integer expression expected
$ [[ "2 + 2" -eq 4 ]] && echo yes
yes

The second subtlety is that, wherever arithmetic evaluation is performed under bash, empty strings evaluate to 0. For example:

$ x=""; echo $((0 + x))
0
$ [[ "" -eq 0 ]] && echo yes
yes

Documentation

From man bash:

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. [Emphasis added]

Aside: Security Issues

Note that bash's arithmetic evaluation is a potential security issue. For example, consider:

x='a[$(rm -i *)]'
[[ x -eq 0 ]] && echo yes

With the -i option, the above is safe but the general lesson is not to use bash's arithmetic evaluation with un-sanitized data.

By contrast, with [, no arithmetic evaluation is performed and, consequently, the command never attempts to delete files. Instead, it safely generates an error:

$ x='a[$(rm -i *)]'
$ [ "$x" -eq 0 ] && echo yes
bash: [: a[$(rm -i *)]: integer expression expected

For more on this issue, see this answer.


Yes, posix test ([) would not convert an string to a number on numerical comparisons:

$ sh -c '[ 2+2 -eq 4 ]'
sh: 1: [: Illegal number: 2+2

$  dash -c '[ 2+2 -eq 4 ]'
dash: 1: [: Illegal number: 2+2

$ bash -c '[ 2+2 -eq 4 ] && echo "YES"'
bash: line 0: [: 2+2: integer expression expected

However, not all shells work in the same way:

$ ksh -c '[ 2+2 -eq 4 ] && echo "YES"'
YES

Usual workaround

Make sure that a null or empty value is converted to 0 (works on most shells)

$ dash -c 'a=""; [ "${a:-0}" -gt 3 ] && echo "YES"'

Use arithmetic

Use arithmetic expansion ( may also convert values as 2+2 in some shells (not dash) )

$ dash -c 'a=""; [ "$((a+0))" -gt -3 ] && echo "YES"'
YES

Use [[

The use of the [[ test will convert most strings that would become a number (even if not wanted) in shells that allow [[:

$ [[ "2+2" -gt 3 ]] && echo "YES"
YES