Different Behavior of Which Command in Zsh and Bash

different behavior of which command in zsh and bash

$ zsh -c 'type which'
which is a shell builtin
$ bash -c 'type which'
which is /usr/bin/which

The solution is to not use which(1), which is a non-standard and not very useful command. The question of what you should use instead isn't the most straightforward due to the alternatives being poorly specified and inconsistently implemented, but they are better than which.

Depending on your requirements, you want command (see the -v option), type, or whence. See POSIX for the former two, or your shell manual for the latter. (Bash doesn't support whence, but it is supported by most other ksh derivatives, albeit inconsistently. It typically has the most features).

different shell behaviors of unmatched glob in zsh and bash

Use setopt NONOMATCH to leave an unmatched glob as literal text.

Similarly, use setopt NULL_GLOB to treat an unmatched glob as if it hadn't been used at all.

Some examples:

% print recommenders[examples]
zsh: no matches found: recommenders[example]
% setopt NONOMATCH
% print recommenders[examples]
recommenders[examples]
% print recommenders[examples]

%

Both options are documented under man zshoptions. (Use man zsh to see what man pages are available, or man zshall to see all of them at once.)

find command works differently in zsh and bash

Try quoting the argument, as in f 'stringBuf*', to avoid premature glob expansion.

If you call it unquoted, bash will do the smart thing and, after looking for the pattern in your current directory and not finding anything, will pass the parameter to the function as is.

zsh on the other hand, will try to match the pattern in your current directory, then complain about not finding anything, and not execute the function at all.

It is generally not a good idea to use unquoted wildcards (unless you mean it), since, if you have a file in your current directory called, say, "stringBuffoon", your parameter to f will be turned into "stringBuffoon", and the search will not give you the results you expect.

Different rm -f behavior between bash and oh-my-zsh

zsh, by default, fails if running a command with a glob with no matches. This behavior contravenes the POSIX sh standard.

bash, by default, follows the POSIX standard, which requires passing the original glob expression to the application in question. Because rm -f is specified to treat the case where a file already doesn't exist identically to the case where it successfully deleted that file, a glob expression with no matches is thus treated as a successful case.

To change this behavior on bash, run:

# tell bash to abort a command rather than passing it an unexpanded glob
shopt -s failglob

Different result from bash and source commands

I don't have a Mac, but here's what I can reproduce on Arch Linux (2021):

$ cat >tt.sh 
listFile=("A" "B" "C" "D")
echo ${listFile[1]} ${listFile[2]}
^D

$ bash tt.sh
B C

$ zsh tt.sh
A B

When I run the script with bash, it shows B C. When I run with zsh, it shows A B.

Are you really using bash on the terminal you're sourcing to? You can double-check with:

$ echo $0
bash

If the above shows bash, it means you are. If it shows zsh, it means you are using zsh so you will see the zsh behaviour when you source.

Starting with macOS Catalina, the default is zsh. If you would really like the bash behaviour, you could try swiching your default shell on Mac settings.

Why does behavior of `./example` and `sh example` different? (`zsh: text file busy: ./example`)

When you do

$ ./example

you are trying to execute "example" which is being written to. This is not allowed.

When you do

$ sh example

sh is reading "example", then execute what is read. This is fine.

different shell behaviour: bash omits newline, zsh keeps it

zsh doesn't perform word-splitting on the unquoted parameter expansion $MATCH by default. Use echo "$MATCHES" | wc -l, and bash should work as well.

Note this is the wrong way to iterate over the output of a command; use a while loop and the read command instead.

grep -ri "todo" apps/business | awk '{print $1}' | sed -e 's/://' -e 's/\#//' |
while IFS= read -r FILE; do
git blame "$FILE" | grep -i todo
done | sort -k2 | uniq


Related Topics



Leave a reply



Submit