38

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. Mar 19, 2011 at 23:43
  • 3
    On mine (linux) it's only which -a. Mar 20, 2011 at 8:59

4 Answers 4

41

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. Nov 25, 2013 at 15:28
  • the type builtin in ksh93u+ 2012-08-01 seems to work properly. Nov 25, 2013 at 15:29
  • Don't forget about whereis
    – nullromo
    Aug 30, 2021 at 16:24
5

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. 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
    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. 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
    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
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.