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.
4 Answers
On some systems, which -a
shows all matches. If your shell is bash or zsh¹, you can use type
instead: type foo
shows the first match and type -a foo
shows all matches. The three commands type
, which
and 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 IFS
and set -f
don't escape the function), and change exit
to return
.
#!/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.
-
That
sh
code doesn't work properly if there are empty components in$PATH
. Also note that$IFS
is a field delimiter (in POSIX shells at least) while in$PATH
, colon is used as a field separator. See thewhich
script found on Debian for a correct implementation. Commented Nov 25, 2013 at 15:28 -
the
type
builtin inksh93u+ 2012-08-01
seems to work properly. Commented Nov 25, 2013 at 15:29 -
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
$PATH
contains whitespace or shell globbing characters.read -r
is necessary to cope with backslashes. This is not a good method becausefind
will take a long time and may return spurious matches if a directory in$PATH
contains subdirectories. Fortunately,find
is not useful here; see my answer. Commented 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. Commented Mar 20, 2011 at 1:25
If you don't have a which
supporting -a
, or 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
-
You're missing
set -f
to turn off globbing on the unprotected$PATH
.test -f
isn't sufficient since only executable files are wanted here; you needtest -x
. Hmm, I realize I forgot the regular file test in my script. Commented Mar 21, 2011 at 21:03 -
@Gilles: edited according to your tips. I'm all for correctness, but I find
whence README.txt
as unlikely aswhence "file* wi?h we!rd name"
. Just trying to show how easy it is to traverse$PATH
. Commented 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. Commented 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.
which --all
.which -a
.