Reputation: 741
Here is a simple test case script which behaves differently in zsh vs bash when I run with $ source test_script.sh
from the command line. I don't necessarily know why there is a difference if my shebang clearly states that I want bash to run my script other than the fact that the which
command is a built-in in zsh and a program in bash. (FYI - the shebang directory is where my bash program lives which may not be the same as yours--I installed a new version using homebrew)
#!/usr/local/bin/bash
if [ "$(which ls)" ]; then
echo "ls command found"
else
echo "ls command not found"
fi
if [ "$(which foo)" ]; then
echo "foo command found"
else
echo "foo command not found"
I run this script with source ./test-script.sh
from zsh and Bash.
Output in zsh:
ls command found
foo command found
Output in bash:
ls command found
foo command not found
My understanding is that default for test
or [ ]
(which are the same thing) evaluate a string to true if it's not empty/null. To illustrate:
zsh:
$ which foo
foo not found
bash:
$ which foo
$
Moreover if I redirect standard error in zsh like:
$ which foo 2> /dev/null
foo not found
zsh still seems to send foo not found
to standard output which is why (I am guessing) my test case passed for both under the zshell; because the expansion of "$(which xxx)"
returned a string in both cases (e.g. /some/directory
and foo not found
(zsh will ALWAYS return a string?).
Lastly, if I remove the double quotes (e.g. $(which xxx)
), zsh gives me an error. Here is the output:
ls command found
test_scritp.sh:27: condition expected not:
I am guessing zsh wanted me to use [ ! "$(which xxx)" ]
. I don't understand why? It never gave that error when running in bash (and isn't this supposed to run in bash anyway?!).
Why isn't my script using bash? Why is something so trivial as this not working? I understand how to make it work fine in both using the -e
option but I simply want to understand why this is all happening. Its driving me bonkers.
Upvotes: 2
Views: 1695
Reputation: 189387
There are two separate problems here.
First, the proper command to use is type
, not which
. Like you note, the command which
is a zsh
built-in, whereas in Bash, it will execute whatever which
command happens to be on your system. There are many variants with different behaviors, which is why POSIX opted to introduce a replacement instead of trying to prescribe a particular behavior for which
-- then there would be yet one more possible behavior, and no way to easily root out all the other legacy behaviors. (One early common problem was with a which
command which would examine the csh
environment, even if you actually used a different shell.)
Secondly, examining a command's string output is a serious antipattern, because strings differ between locales ("not found" vs. "nicht gefunden" vs. "ei löytynyt" vs. etc etc) and program versions -- the proper solution is to examine the command's exit code.
if type ls >/dev/null 2>&1; then
echo "ls command found"
else
echo "ls command not found"
fi
if type foo >/dev/null 2>&1; then
echo "foo command found"
else
echo "foo command not found"
fi
(A related antipattern is to examine $?
explicitly. There is very rarely any need to do this, as it is done naturally and transparently by the shell's flow control statements, like if
and while
.)
Regarding quoting, the shell performs whitespace tokenization and wildcard expansion on unquoted values, so if $string
is command not found
, the expression
[ $string ]
without quotes around the value evaluates to
[ command not found ]
which looks to the shell like the string "command" followed by some cruft which isn't syntactically valid.
Lastly, as we uncovered in the chat session (linked from comments) the OP was confused about the precise meaning of source
, and ended up running a Bash script in a separate process instead. (./test-script
instead of source ./test-script
). For the record, when you source
a file, you cause your current shell to read and execute it; in this setting, the script's shebang line is simply a comment, and is completely ignored by the shell.
Upvotes: 5