1. Overview
which is a common tool on Linux systems for locating the executable for a given command. We can use which to check if there’s a command on our system and its configured paths. However, many experienced users don’t recommend the use of which.
In this tutorial, we’ll learn what which does, why it can cause problems, and some alternatives we can use instead.
Also, we ran this code on Ubuntu 20.04.3 with root permission to execute the commands.
2. which Command
which is a simple tool that allows us to find out where a certain command or program is located on our system. It takes one or more arguments. The arguments are the names of the commands that we want to locate. Then, which searches for each argument in each directory from the list of colon-separated paths specified in the $PATH environment variable. Finally, the output contains the full path of the first, if any, match found for each argument.
For instance, let’s find out where the ls command is located:
$ which ls
/usr/bin/ls
This shows that the ls command is located in /usr/bin/ls.
If we pass multiple arguments to which, it outputs the found path for each one in the same order.
Let’s see where the grep, awk, and sed executables are:
$ which grep awk sed
/usr/bin/grep
/usr/bin/awk
/usr/bin/sed
In the result, the location of each command is displayed in the same order we passed them to which.
Notably, which has a single option, -a, for printing all matches for each argument, and not just the first one. In fact, this can be useful if there’re multiple versions of the same program in different directories.
For instance, we can find out all the matches for the python3 interpreter:
$ which -a python3
/usr/bin/python3
/bin/python3
The result shows both matches for python3.
3. Drawbacks of which
Although which may seem like a handy tool for finding commands on Linux systems, it has some limitations and drawbacks that can make it unreliable.
3.1. which and Shell Differences
One of the problems with using which is that it behaves differently depending on the shell that’s used. Notably, different shells may have different implementations of which, or may not have it at all.
For instance, zsh has a built-in which command that can show aliases, functions, and built-ins, while Bash doesn’t. Some shells may use an external which command that’s a script or a binary file, while others may use an internal which command that’s part of the shell itself.
So, this means that using which can give inconsistent results depending on the shell that’s used.
For instance, let’s see the behavior of running which in Bash if we have an alias called ls that runs the command ls –color=auto:
$ which ls
/usr/bin/ls
The output from Bash shows the path of the original ls command, but it doesn’t show the alias that we’ve defined.
However, the behavior is quite different in zsh:
$ which ls
ls: aliased to ls --color=auto
This shows the alias that we’ve defined, but it doesn’t show the path of the original ls command.
3.2. which and Environment Variables
Besides, another problem is that which relies on the $PATH environment variable to search for commands. Thus, $PATH can affect the accuracy and reliability of the output.
The $PATH environment variable is a list of directories that the shell searches in when looking for commands to run. The order of the directories in the $PATH variable matters. In fact, the shell stops searching once it finds a match for a given command name.
However, the $PATH variable can be modified, corrupted, or overridden by other variables or scripts. Obviously, this can affect how which works.
For instance, let’s say we have two versions of Python installed on our system:
- /usr/bin/python3
- /usr/local/bin/python3
The latter one is newer and has more features than the former one. If we want to use the newer version of Python by default, we can add /usr/local/bin to the beginning of our $PATH variable. This makes its priority higher than that in /usr/bin.
3.3. Caching
Yet, if we run which python3, we may get /usr/bin/python3 as the output even if a python3 instance exists in a preceding $PATH directory. This is because which may be using a cached or outdated version of the $PATH variable that doesn’t reflect our changes.
Alternatively, if we run a script that changes or overrides our $PATH variable temporarily, running which python3 may give us a different output than before or no output at all.
Consequently, using which can be inaccurate and unreliable when searching for commands based on the $PATH environment variable alone.
3.4. which and Aliases
In shells other than tcsh and zsh, the behavior of which with aliases depends on how the aliases are defined and how the $PATH environment variable is set. In a shell like Bash, which doesn’t recognize or report aliases correctly. Ultimately, this can lead to inaccurate results, especially if we declared the aliases interactively.
However, if which is a shell built-in as is the case in zsh, it will try to reconstruct the aliases from the shell’s startup files.
For instance, let’s create an alias for grep and run which with the alias as an argument:
$ alias grep='grep -E'
$ which grep
/usr/bin/grep
Here, we only see the location of the grep command. However, this is not what we want if we need to know the exact command that’ll be executed when we type grep in our shell.
Additionally, if we create an alias called gr that runs the command grep -E, which won’t return any location.
4. Alternatives to which
Of course, there’re some other commands that can help us find or verify executable locations on Linux systems.
4.1. The type Command
The type command is a shell built-in that displays information about how a given command name would be interpreted by the shell. It can show whether a name is an alias, a function, or a built-in command:
$ type ls
ls is aliased to `ls --color=auto'
This output shows that ls is an alias that runs the command ls –color=auto.
Furthermore, we can use type to see if a command is a shell built-in:
$ type echo
echo is a shell builtin
In this case, echo is a built-in command that’s part of the shell itself.
Additionally, type can show the path of an external command:
$ type python3
python3 is /usr/bin/python3
Here, we see that python3 is an external command and its path is /usr/bin/python3.
Also, we can use type to show the path to a command only if the command is an executable file using the -p option:
$ type -p grep sed awk
/usr/bin/grep
/usr/bin/sed
/usr/bin/awk
The type command can also show all the matches for a given name by using the -a option:
$ type -a python3
python3 is /usr/bin/python3
python3 is /bin/python3
The type command is often more reliable and informative than which. However, the type command may not be available in all shells or systems.
4.2. The command Command
The command command is similar to the type command. For instance, the -V option of command is equivalent to type without any option. However, command has some differences in syntax and functionality.
Of course, command can show the path of an external command or an executable by using the -v option:
$ command -v grep sed awk
/usr/bin/grep
/usr/bin/sed
/usr/bin/awk
In particular, we can employ command to execute a program without invoking any shell function, built-in, or alias:
$ command ls
bin dev home lib32 libx32 mnt proc run srv tmp var
boot etc lib lib64 media opt root sbin sys usr
Notably, this executes the ls command without any interference from shell functions that may have been defined with the same name.
4.3. The whereis Command
The whereis command is an external program that shows the path of a command file. Additionally, whereis shows the manual page file of the command passed to it as an argument.
For instance, let’s find the path of the ls command file as well as its manual page file:
$ whereis ls
ls: /usr/bin/ls /usr/share/man/man1/ls.1.gz /usr/share/man/man1/ls.1posix.gz
This shows that the path of the ls command file is /usr/bin/ls. Furthermore, it shows that the path of its manual page file is /usr/share/man/man1/ls.1.gz. Finally, we can see an additional manual page file for the ls command that follows the POSIX standard.
Furthermore, we can use the -b option to see only the executable path.
5. Conclusion
In this article, we’ve discussed the which command and why its use isn’t generally recommended.
which is a common tool for system administrators to use when looking for the location of a command executable. However, it’s sometimes better to avoid which because it may have unexpected behavior in different shell environments.
Additionally, we’ve seen the behavior of which with aliases and how the environment variables affect it. Finally, we discussed some alternatives to which that we can use to find command executables on Linux systems.