Why does `==` behave differently inside `[ ... ]` in zsh and bash?

It's not /usr/bin/[ in either of the shells. In Bash, you're using the built-in test/[ command, and similarly in zsh.

The difference is that zsh also has an = expansion: =foo expands to the path to the foo executable. That means == is treated as trying to find a command called = in your PATH. Since that command doesn't exist, you get the error

zsh: = not found

that you saw (and in fact, this same thing would happen even if you actually were using /usr/bin/[).

You can use == here if you really want. This works as you expected in zsh:

[ "a" "==" "a" ] && echo yes

because the quoting prevents =word expansion running. You could also disable the equals option with setopt noequals.

However, you'd be better off either:

  • Using single =, the POSIX-compatible equality test; or
  • Better still, using the [[ conditionals with == in both Bash and zsh. In general, [[ is just better and safer all around, including avoiding this kind of issue (and others) by having special parsing rules inside.

And zsh, and bash give the same answer (type is builtin too for both shells):

$ type -a [
[ is a shell builtin
[ is /usr/bin/[

[ is a shell builtin command in bash and in zsh:

$ type [
[ is a shell builtin

From the Shell Builtin Commands documentation:

Builtin commands are contained within the shell itself. When the name of a builtin command is used as the first word of a simple command (see Simple Commands), the shell executes the command directly, without invoking another program. Builtin commands are necessary to implement functionality impossible or inconvenient to obtain with separate utilities.

The official documentation ($ help test) only allows to use =:


True if the strings are equal.

So, the correct expression would be:

$ [ "a" = "a" ] && echo yes

What happens is that bash is a bit less strict. Supporting the == operator with [ seems to be a bash extension and it is no recommended to use it:

string1 == string2

string1 = string2

True if the strings are equal. When used with the [[ command, this performs pattern matching as described above (see Conditional Constructs).

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

If you want to use ==, you should use the [[ keyword:

$ [[ "a" == "a" ]] && echo yes

Keep in mind that [[ is less portable (is not POSIX). But both bash and zsh support it.