Shell equality operators (=, ==, -eq)
It's the other way around: =
and ==
are for string comparisons, -eq
is for numeric ones. -eq
is in the same family as -lt
, -le
, -gt
, -ge
, and -ne
, if that helps you remember which is which.
==
is a bash-ism, by the way. It's better to use the POSIX =
. In bash the two are equivalent, and in plain sh =
is the only one guaranteed to work.
$ a=foo
$ [ "$a" = foo ]; echo "$?" # POSIX sh
0
$ [ "$a" == foo ]; echo "$?" # bash specific
0
$ [ "$a" -eq foo ]; echo "$?" # wrong
-bash: [: foo: integer expression expected
2
(Side note: Quote those variable expansions! Do not leave out the double quotes above.)
If you're writing a #!/bin/bash
script then I recommend using [[
instead. The doubled form has more features, more natural syntax, and fewer gotchas that will trip you up. Double quotes are no longer required around $a
, for one:
$ [[ $a == foo ]]; echo "$?" # bash specific
0
See also:
- What's the difference between [ and [[ in Bash?
It depends on the Test Construct around the operator. Your options are double parentheses, double brackets, single brackets, or test
.
If you use ((
…))
, you are testing arithmetic equality with ==
as in C:
$ (( 1==1 )); echo $?
0
$ (( 1==2 )); echo $?
1
(Note: 0
means true
in the Unix sense and a failed test results in a non-zero number.)
Using -eq
inside of double parentheses is a syntax error.
If you are using [
…]
(or single brackets) or [[
…]]
(or double brackets), or test
you can use one of -eq
, -ne
, -lt
, -le
, -gt
, or -ge
as an arithmetic comparison.
$ [ 1 -eq 1 ]; echo $?
0
$ [ 1 -eq 2 ]; echo $?
1
$ test 1 -eq 1; echo $?
0
The ==
inside of single or double brackets (or the test
command) is one of the string comparison operators:
$ [[ "abc" == "abc" ]]; echo $?
0
$ [[ "abc" == "ABC" ]]; echo $?
1
As a string operator, =
is equivalent to ==
. Also, note the whitespace around =
or ==
: it’s required.
While you can do [[ 1 == 1 ]]
or [[ $(( 1+1 )) == 2 ]]
it is testing the string equality — not the arithmetic equality.
So -eq
produces the result probably expected that the integer value of 1+1
is equal to 2
even though the right-hand side is a string and has a trailing space:
$ [[ $(( 1+1 )) -eq "2 " ]]; echo $?
0
While a string comparison of the same picks up the trailing space and therefore the string comparison fails:
$ [[ $(( 1+1 )) == "2 " ]]; echo $?
1
And a mistaken string comparison can produce a completely wrong answer. 10
is lexicographically less than 2
, so a string comparison returns true
or 0
. So many are bitten by this bug:
$ [[ 10 < 2 ]]; echo $?
0
The correct test for 10
being arithmetically less than 2
is this:
$ [[ 10 -lt 2 ]]; echo $?
1
In comments, there is a question about the technical reason why using the integer -eq
on strings returns true for strings that are not the same:
$ [[ "yes" -eq "no" ]]; echo $?
0
The reason is that Bash is untyped. The -eq
causes the strings to be interpreted as integers if possible including base conversion:
$ [[ "0x10" -eq 16 ]]; echo $?
0
$ [[ "010" -eq 8 ]]; echo $?
0
$ [[ "100" -eq 100 ]]; echo $?
0
And 0
if Bash thinks it is just a string:
$ [[ "yes" -eq 0 ]]; echo $?
0
$ [[ "yes" -eq 1 ]]; echo $?
1
So [[ "yes" -eq "no" ]]
is equivalent to [[ 0 -eq 0 ]]
Last note: Many of the Bash specific extensions to the Test Constructs are not POSIX and therefore may fail in other shells. Other shells generally do not support [[...]]
and ((...))
or ==
.
==
is a bash-specific alias for =
and it performs a string (lexical) comparison instead of a numeric comparison. eq
being a numeric comparison of course.
Finally, I usually prefer to use the form if [ "$a" == "$b" ]
Several answers show dangerous examples. The OP's example, [ $a == $b ]
, specifically used unquoted variable substitution (as of the October 2017 edit). For [...]
that is safe for string equality.
But if you're going to enumerate alternatives like [[...]]
, you must inform also that the right-hand-side must be quoted. If not quoted, it is a pattern match! (From the Bash man page: "Any part of the pattern may be quoted to force it to be matched as a string.").
Here in Bash, the two statements yielding "yes" are pattern matching, other three are string equality:
$ rht="A*"
$ lft="AB"
$ [ $lft = $rht ] && echo yes
$ [ $lft == $rht ] && echo yes
$ [[ $lft = $rht ]] && echo yes
yes
$ [[ $lft == $rht ]] && echo yes
yes
$ [[ $lft == "$rht" ]] && echo yes
$