46

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.

2
  • 1
    On my system, it's the first thing listed on the man page: which --all. Commented Mar 19, 2011 at 23:43
  • 3
    On mine (linux) it's only which -a. Commented Mar 20, 2011 at 8:59

4 Answers 4

48

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.

3
  • 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 the which script found on Debian for a correct implementation. Commented Nov 25, 2013 at 15:28
  • the type builtin in ksh93u+ 2012-08-01 seems to work properly. Commented Nov 25, 2013 at 15:29
  • Don't forget about whereis
    – nullromo
    Commented Aug 30, 2021 at 16:24
6

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
2
  • 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 because find 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.
    – Eli Heady
    Commented Mar 20, 2011 at 1:25
2

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
3
  • 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 need test -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 as whence "file* wi?h we!rd name". Just trying to show how easy it is to traverse $PATH.
    – MattBianco
    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
0

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .