I think most are familiar with the
which command, and I use it frequently. I just ran into a situation where I'm curious not just which command is first in my path, but how many and where all the commands in all my paths are. I tried the which man page (typing
man which made me laugh), but didn't see anything.
I think most are familiar with the
On some systems,
which -a shows all matches. If your shell is bash or zsh¹, you can use
type foo shows the first match and
type -a foo shows all matches. The three commands
whence do mostly the same thing; they differ between shells and operating systems in availability, options, and what exactly they report.
type is always available and shows all possible command-like names (aliases, keywords, shell built-ins, functions, and external commands).
The only fully portable way to display all matches is to parse
$PATH yourself. Here's a shell script that does this. If you make it a shell function, make sure to enclose the function body in parentheses (so that the change to
set -f don't escape the function), and change
#!/bin/sh set -f # disable globbing IFS=: # break words at : only not_found=1 for d in $PATH; do if [ -f "$d/$x" ] && [ -x "$d/$x" ]; then printf '%s\n' "$d/$x" not_found=0 fi done exit $not_found
¹ Or ksh 93, according to the documentation, though ksh 93s+ 2008-01-31 only prints the first match when I try.
shcode doesn't work properly if there are empty components in
$PATH. Also note that
$IFSis a field delimiter (in POSIX shells at least) while in
$PATH, colon is used as a field separator. See the
whichscript found on Debian for a correct implementation. Nov 25, 2013 at 15:28
ksh93u+ 2012-08-01seems to work properly. Nov 25, 2013 at 15:29
Don't forget about
whereis– nullromoAug 30, 2021 at 16:24
The --all or -a flag will show you all matches in your path, and aliases (at least on Fedora, Ubuntu and CentOS):
which -a which
On AIX and Solaris, this will get you close:
echo "$PATH" | sed -e 's/:/ /g' | \ while read -r p; do find "$p" -type f -name "which"; done
You need double quotes around parameter substitutions, otherwise the script won't work if
$PATHcontains whitespace or shell globbing characters.
read -ris necessary to cope with backslashes. This is not a good method because
findwill take a long time and may return spurious matches if a directory in
$PATHcontains subdirectories. Fortunately,
findis not useful here; see my answer. Mar 20, 2011 at 0:30
I knew find felt wrong since it would certainly be slow in nested directories. Globbing and spaces in $PATH? eww. But you're right (though you were nice enough not to say as much): my one liner was poorly written. Mar 20, 2011 at 1:25
If you don't have a
whence available, roll your own:
#!/bin/sh -f IFS=":" for PART in $PATH do if test -x "$PART/$1" then echo $PART/$1 fi done
set -fto turn off globbing on the unprotected
test -fisn't sufficient since only executable files are wanted here; you need
test -x. Hmm, I realize I forgot the regular file test in my script. Mar 21, 2011 at 21:03
@Gilles: edited according to your tips. I'm all for correctness, but I find
whence README.txtas unlikely as
whence "file* wi?h we!rd name". Just trying to show how easy it is to traverse
$PATH. Mar 22, 2011 at 8:58
This was the only script that worked for me in OmniOS r151030 (OpenSolaris fork). #!/bin/sh -x helpful for troubleshooting - is there a way to omit the last echo? It's a repeat. Jan 19, 2021 at 21:22
ksh and zsh have "whence" as a shell built-in.
whence -a does what you want under zsh:
7:27AM 7 % whence -a cat /usr/bin/cat /bin/cat /usr/bin/cat /bin/cat
I have to clean up PATH in zsh, I have lots of duplicates in it.
whence -a works differently under ksh:
$ whence -a cat cat is a tracked alias for /usr/bin/cat
I have to say, that seems like a potentially useful behavior, too.