MochiMoppel wrote: Fri Apr 21, 2023 12:55 am
I wonder if all shells would work the same when you don't check the output string of the which command, which is irrelevant here anyway, but rather its exit code. IMO this would be cleaner and more reliable code.
Code: Select all
sh -c 'which blablablah >/dev/null || echo "blablablah not found"'
fish -c 'which blablablah >/dev/null || echo "blablablah not found"'
flzsh -c 'which blablablah >/dev/null || echo "blablablah not found"'
I think so. Every *nix program has an exit code, and every shell (that I know of) returns it when the program terminates. I don't know if all shells return the exit code in the $? variable and provide the || and && operators, but the ones in use in this forum do (the Bourne shell derivatives, of which fish isn't). In addition to the way you wrote the above code, which I would certainly use in my own scripts, I also use other forms depending on the context, such as
Code: Select all
command -v cmd > /dev/null || echo cmd not found # builtin; can also use 'type' instead of 'command'
if command -v cmd > /dev/null; then
echo cmd found
else
echo cmd not found
fi
I prefer the if-then-else form because it gives me complete control on the value that $? can take after the 'fi' keyword. That is, I can add more commands in the then and else bodies and affect $? with those. This makes the whole if-then-else chainable with || and &&, or simply by checking the value of $? after 'fi'. Another advantage is that it makes using the shell's -e flag easier and more clear.
As you mentioned earlier in this thread, writing stuff like [ -z "$(cmd ...)" ] is more complex, less portable and less-efficient (because it spawns a sub-shell needlessly).
One use case for preferring 'which' to a similar builtin is when you want to take the first of a series of equally viable commands. That is, 'which cmd1 cmd2 cmd3 cmd4' outputs the first installed among cmd1,...,cmd4. This could be cmd2 on a system, cmd1 on another, and so on. Extension: When the cmds are equivalent but need different arguments to be so, 'case-esac' can be used on the output of a single 'which' invocation.
I tested the code you wrote above with fish and zsh and they both print "blablablah not found". Fish also prints an error message from which. Zsh doesn't because which is a builtin. However, zsh's 'where' is the idiom for 'which', which leads us to who's on first for a closing.