9

From bash manual, for conditional expressions

string1 == string2
string1 = string2

True if the strings are equal.

  1. When used with the [[ command, this performs pattern matching as described above (see Section 3.2.4.2 [Conditional Constructs], page 10).

    • What does "pattern matching" mean here?

    • What is "pattern matching" opposed to here?

    • If not used with [[ but with other commands, what does "this" perform?

  2. ‘=’ should be used with the test command for posix conformance.

    • What does POSIX say here?

    • What is the sentence opposed to?

    • Can == be used with test command? I tried and it seems yes.

    • Can = be used with other commands besides test? I tried = with [[ and [, and it seems yes.

  3. what are the differences between == and =?

    In Bash 4.3, I tried == and = with test, [[, and [. == and = look the same to me.

    Can == and = be used interchangeably in any conditional expression?

Thanks.

Tim
  • 101,790

2 Answers2

16

POSIX test (or [ ... ]) only knows about the one with a single equal sign:

s1 = s2
True if the strings s1 and s2 are identical; otherwise, false.

But Bash accepts the double equal sign too, though the builtin help doesn't admit to that (the manual does):

$ help test | grep -A1 =
  STRING1 = STRING2
                 True if the strings are equal.
  STRING1 != STRING2
                 True if the strings are not equal.

As for other shells, it depends. Well, particularly Dash is the stubborn one here:

$ dash -c '[ x == x ] && echo foo'
dash: 1: [: x: unexpected operator

but

$ yash -c '[ x == x ] && echo foo'
foo
$ busybox sh -c '[ x == x ] && echo foo'
foo
$ ksh93 -c '[ x == x ] && echo foo'
foo

In zsh, =something is a filename expansion operator that expands to the path of the something command if found in $PATH unless the equals option is turned off (like when emulating other shells), and that also applies to == or =~ when found in arguments to simple commands such as test or [ so at least the leading = must be quoted there:

$ zsh -c 'echo =ls'
/usr/bin/ls

$ zsh +o equals -c 'echo =ls' =ls

$ zsh --emulate ksh -c 'echo =ls' =ls

$ zsh -c '[ x == x ] && echo foo' zsh:1: = not found

$ zsh -c '[ x "==" x ] && echo foo' foo

The external test/[ utility from GNU coreutils on my Debian supports == (but the manual doesn't admit that), the one on OS X doesn't.

So, with test/[ .. ], use = as it's more widely supported.


With the [[ ... ]] construct, both = and == are equal (at least in Bash) and the right side of the operator is taken as a pattern, like in a filename glob, unless it is quoted. (Filenames are not expanded within [[ ... ]])

$ bash -c '[[ xxx == x* ]] && echo foo'
foo

But of course that construct isn't standard:

$ dash -c '[[ xxx == x* ]] && echo foo'
dash: 1: [[: not found
$ yash -c '[[ xx == x* ]] && echo foo'
yash: no such command ‘[[’

And while Busybox has it, it does't do the pattern match:

$ busybox sh -c '[[ xx == xx ]] && echo yes || echo no'
yes
$ busybox sh -c '[[ xx == x* ]] && echo yes || echo no'
no
ilkkachu
  • 138,973
  • 1
    Thanks. Do you mean that [[ ... ]] doesn't exist in POSIX shell, and == doesn't exist in POSIX shell either? – Tim Jul 26 '17 at 21:27
  • 1
    @Tim, yes, that's correct. Although "exist" is a slippery word. "Aren't specified by POSIX" is more correct. POSIX is a set of specifications, not an actual shell. – Wildcard Jul 27 '17 at 00:31
  • Try help [[ and search for ==. –  Jul 27 '17 at 01:38
  • zsh's [ supports ==. It's just that =something is a special operator (when not emulating other shell), so needs quoted. Same problem with =~: [ x '=~' . ], [ x '==' x ]. – Stéphane Chazelas Jul 27 '17 at 06:23
  • @StéphaneChazelas, I did wonder about that, and the confusing error message, thanks for the clarification! – ilkkachu Jul 27 '17 at 10:20
6

In bash, there are four conditions about equality:

  • The simple and most basic (and only posix compatible) = inside [ … ] (or test):
    Only performs equality (byte by byte) of two strings.

     STRING1 = STRING2
                 True if the strings are equal.
    
  • The extended == . Which still performs (only) an equality test.

    $ [ aaaa == aaaa ] && echo yes
    yes
    
    $ [ aaaa == a* ] && echo yes
    $
    

    Be careful that the unquoted a* will be expanded to a filename (or several) if a matching filename exist in the pwd. In specific: an existing file named aaaa will make the code output yes. If there are no files matching, the exact comparison is afected by the failglob and nullglob shell options.

  • A = inside a [[ is exactly equivalent to:

  • A == inside a [[ does both byte-by-byte and glob matching.

    If the string or variable on the right side of the == is quoted, a byte comparison is made. If all the bytes are equal, the result of the [[ is "good" (0).

    If the string, or preferable in all cases: a variable, is unquoted, the match is performed as in a filename glob.

    $ [[ aaaa == "aaaa" ]] && echo yes
    yes
    
    $ a='aaaa'
    $ [[ aaaa == "$a" ]] && echo yes
    yes
    
    $ a='a*'
    $ [[ aaaa == "$a" ]] && echo yes
    $
    
    $ a='a*'
    $ [[ aaaa == $a ]] && echo yes
    yes
    

It is interesting to note that the unquoted aaaa also work:

$ a='aaaa'
$ [[ aaaa = $a ]] && echo yes
yes

This happens because the string inside the variable does not have any expandable glob characters *, +, ?, [ and the extended (if activated) |, @ and ! . But that is usually a risky bet to use.