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 =
:
STRING1 = STRING2
True if the strings are equal.
So, the correct expression would be:
$ [ "a" = "a" ] && echo yes
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
yes
Keep in mind that [[
is less portable (is not POSIX). But both bash and zsh support it.