Shell equality operators (=, ==, -eq)
Asked Answered
D

4

351

What is the difference between =, == and -eq in shell scripting?

Is there any difference between the following?

[ $a = $b ]
[ $a == $b ]
[ $a -eq $b ]

Is it simply that = and == are only used when the variables contain numbers?

Deyoung answered 8/12, 2013 at 3:29 Comment(0)
F
474

= and == are for string comparisons
-eq is for numeric comparisons
-eq is in the same family as -lt, -le, -gt, -ge, and -ne

== is specific to bash (not present in sh (Bourne shell), ...). Using POSIX = is preferred for compatibility. In bash the two are equivalent, and in sh = is the only one that will 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

(Note: make sure to quote the variable expansions. Do not leave out the double-quotes above.)

If you're writing a #!/bin/bash script then I recommend using [[ instead. The double square-brackets [[...]] form has more features, a more natural syntax, and fewer gotchas that will trip you up. For example, double quotes are no longer required around $a:

$ [[ $a == foo ]]; echo "$?"      # bash-specific
0

See also:

Flagstaff answered 8/12, 2013 at 3:31 Comment(0)
H
65

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

Hem answered 4/11, 2016 at 20:37 Comment(4)
I'm curious about the technical reason for [[ "yes" -eq "no" ]] returning True. How does bash coerce these strings to integer values that can be compared? ;-)Outwards
Bash variables are untyped so [[ "yes" -eq "no" ]] is equivalent to [[ "yes" -eq 0 ]] or [[ "yes" -eq "any_noninteger_string" ]] -- All True. The -eq forces integer comparison. The"yes" is interpreted as a integer 0; the comparison is True if the other integer is either 0 or the string result is 0.Hem
Boo, hiss re: showing (nonportable) == in the code samples and only mentioning (portable, standardized) = underneath.Phio
@CharlesDuffy: To be fair -- the post is tagged Bash.Hem
B
31

== 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" ]

Breathed answered 8/12, 2013 at 3:33 Comment(5)
Using == here is bad form, as only = is specified by POSIX.Phio
If you really insist to use == then put it in between [[ and ]]. (And make sure that the first line of your script specifies to use /bin/bash.)Pagandom
@Pagandom thank you for the thorough and comprehensive explanation.Shennashensi
What would you do with either $a evaluated to a empty string or $b does?Peep
@Peep the double quotes in my comparison are not ceremonial.Breathed
D
25

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
$
Denton answered 15/1, 2018 at 3:2 Comment(1)
Needs to be [ "$lht" = "$rht" ] with the quotes to be reliable even for equality. If you have a file created with touch 'Afoo -o AB', [ $lft = $rht ] will return true, even though that file name is not at all identical to AB.Phio

© 2022 - 2024 — McMap. All rights reserved.