Why does `==` behave differently inside `[ ... ]` in zsh and bash?
Solution 1
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.
Solution 2
And zsh, and bash give the same answer (type
is builtin too for both shells):
$ type -a [
[ is a shell builtin
[ is /usr/bin/[
Solution 3
[
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.
Solution 4
In both shells, bash
and zsh
, the [
utility is a shell builtin. This is the shells implementation of that tool, it is used in preference to the binary /usr/bin/[
. The different results you encounter is caused by different implementations.
In bash
, the [
utility accepts CONDITIONAL EXPRESSIONS
as the [[
compound command. According to bashs man page both =
and ==
are valid:
string1 == string2
string1 = string2
True if the strings are equal. = should be used with the test command for POSIX
conformance.
In zsh
, the [
utility attempts to implement POSIX and its extensions where these are specified. In the specification of the POSIX test utility there is no ==
operator defined.
Related videos on Youtube
![Johnson Steward](https://i.stack.imgur.com/A7cry.png?s=256&g=1)
Johnson Steward
Updated on September 18, 2022Comments
-
Johnson Steward almost 2 years
I get what I expected when doing this in
bash
:[ "a" == "a" ] && echo yes
It gave me
yes
.But when I do this in
zsh
, I get the following:zsh: = not found
Why does the same command (
/usr/bin/[
) behave differently in different shells?-
mikeserv over 8 yearsit's not the same command - its a builtin found before
$PATH
is searched. and==
isn't validtest
syntax for the/usr/bin/[
anway. Just=
is fine.
-
-
Johnson Steward over 8 yearsWhat's the exact difference between
[
and[[
? (Searching on Google for[ vs [[
gives me results forvs
only, LOL) -
Michael Homer over 8 years
[[
supports a wider range of tests in both cases, and has custom parsing rules that avoid the need for quoting variables, operators, and so on. If you're specifically using either Bash or zsh, use[[
. If you're writing a portable script, write to the POSIX-compatible[
/test
command (which may or may not be a real command on your running system). -
Johnson Steward over 8 yearsi'm doing this inside my
zshrc
for exporting$TERM
when usingxfce4-terminal
(replacingxterm
toxterm-256color
) -
Michael Homer over 8 yearsJust use
[[
there and forget[
exists. -
mikeserv over 8 yearsyou know... i disagree with your recommendation.
[[
is differently capable. try this with it:for f in *; do [ -e "$f" ] && for a in f d h p S b c; do [ "-$a" "$f" ] && for p in r w x u g; do [ "-$p" "$f" ] || p=-; a=$a$p; done && break; done && printf "%s:\t%s\n" "$a" "$f"; done
. -
Michael Homer over 8 years@mikeserv: Oh, absolutely. If you're not trying to do something clever, you're much less likely to shoot yourself in the foot with
[[
, though. It's good advice to anyone who needs the advice, and anyone who doesn't need it knows why I'm wrong. -
mikeserv over 8 years^with that I completely agree. i dont much like the way
[[
refuses to be scripted - everything has to be explicit on its command-line. it's why i like[
because it accepts arguments like most others. but,[[
is a lot less to worry about if you like to do a lot of typing. though that thing isn't all that clever - it just loops over normal file attributes. -
mikeserv over 8 yearsanyway.,, mine was your second upvote. agreement aside : its a good answer, and the information is useful, as usual.
-
Stéphane Chazelas over 8 yearsWell,
=
/==
is arguably one case where[[
is not better than[
, as[[ a == b ]]
is does "a" match the "b" pattern and not is "a" equal to "b" as you'd expect. That means you need to write[[ $a == "$b" ]]
for instance. At least, with the[
command, if you know how command parsing works, you know you need to write it[ "$a" = "$b" ]
to prevent the split+glob operator like in other commands. With that in mind and if you don't use -a or -o,[
is safe in POSIX conformant implementations. -
mikeserv over 8 years@CharlesDuffy - well, i'd argue that's boring - but im a jerk. you're entitled to your opinion, but i think that loop above is a very good example of how
[
and[[
are differently capable - regardless of your stylistic choices. and it think, i fact, that your argument supports that as well - your stylistic choices are more favorable to one than the other - and so they must differ in capability somewhat...