The which
command is a relic of a bygone Unix era that should have died a long time ago.
When I’ve suggested to “experienced programmers” that they should stop using which
, and use something more reliable, they look at me like I’m the crazy one, and point out that “it’s more portable than all the alternatives” (or just insult my parentage).
Typically what they really mean is “it’s portable to some ancient pre-POSIX version of Solaris”, or “it’s portable to BusyBox”. OK, so if you care so much about portability to those crippled non-standard platforms that you’re willing to forego portability to more standard platforms, you’re welcome to go play in the cesspit on your own; have fun.
which
?which
is supposed to tell you which program would be run if you simply typed its name into the shell. What it actually does is tell you where it might find the command in $PATH
, which means it’s OK for interactive use as long as you understand that limitation.
The thing it’s not designed to do, but which is by far its most common use-case, is to tell a script what program would be invoked by the script if it invoked the program by name later on. POSIX does not require the output of which
to be usable for this or any particular purpose; merely that it “indicates” the path of the program corresponding to each argument.
There are broadly two reasons why people use which
in their scripts.
The fix to the first reason is to use command -v
, because POSIX requires it to have a useful exit status. (And it also requires its output to be exactly the path to the program, no more, no less.)
The fix to the second reason is simply: don’t.
Don’t write CAT=
`which cat
` and then $CAT
.
Simply writing cat
will work just as well, just as fast, and more reliably than $CAT
.
The first thing to know about which
is that it’s a separate binary program, not part of whichever shell you’re using, either interactively, or as the interpreter for your script. Therefore it does not a priori know the internal state of your shell, and that means it’s always going to be an approximation.
A basic version of which
will simply assume you want a program that’s found in $PATH
. Most of the time that’ll be true, but this approach will forceably bypass anything that’s not an external binary:
It will also fail to take into account things that might change between when you invoke which
and when you actually want to invoke the command:
PATH
can be changedPATH
includes a relative path.There are also enhanced versions of which
that have even more problems.