1. Overview

which is a built-in shell command. It can tell us the full path of an executable, for example:

$ which vim
/usr/bin/vim

In this quick tutorial, we’ll see how to get an executable’s path when it’s in an alias.

2. Introduction to the Problem

Knowing the full path of an executable is pretty helpful if we have multiple commands or executables with the same filename. Let’s see an example:

$ which -a echo
echo: shell built-in command
/usr/bin/echo

In the example above, we’ve used which with the -a option to list all matching executables in the PATH variable. So, the system has two echo commands.

Next, let’s run the command again without the -a option:

$ which echo
echo: shell built-in command

The output tells us if we execute echo in the command line, the built-in echo will be used.

In practice, we often use aliases to create some shortcuts for executable combinations or executables with commonly used options. For example, we can create an alias to make our grep command always show matched text in color:

$ alias grep
grep='grep --color=auto'

Aliases are convenient. However, if we check the “grep” alias with the which command, it cannot report the full path of the real grep command anymore:

$ which grep
grep: aliased to grep --color=auto

So next, we’ll learn how to get the full path of the actual command in an alias.

3. Using the type Command

type is also a shell built-in command. It can examine if an executable is an alias or a function:

$ type grep
grep is an alias for grep --color=auto

For Bash’s type, if we add the -P option, the type command reports the path of the command:

$ type -P grep
/usr/bin/grep

However, if our shell is Zsh, we need to use the lowercased -p option:

$ type -p grep
grep is /usr/bin/grep

4. Using the GNU which Command

We’ve mentioned which is a shell built-in command:

$ which which
which: shell built-in command

However, there’s another GNU which (/usr/bin/which) command:

$ which -a which
which: shell built-in command
/usr/bin/which

The GNU which command provides more options, especially since it has the –read-alias (-i) and the –skip-alias options to find the path of the executable in an alias.

Next, let’s see how to use these two options:

$ alias | /usr/bin/which -i grep
grep='grep --color=auto'
	/usr/bin/grep

$ /usr/bin/which --skip-alias grep
/usr/bin/grep

As we can see, GNU which with the -i option reads the aliases from stdin, shows the details of the grep alias and the path of the command grep. However, the –skip-alias option tells GNU which to search for normal binaries. –skip-alias is also the default setting.

Further, if an alias contains multiple commands, GNU which with -i reports the path of each command. Let’s say we have an alias to list the top 10 frequently used commands:

$ alias top10
top10='print -l  ${(o)history%% *} | uniq -c | sort -nr | head -n 10'

Now, let’s check the top10 alias using GNU which with -i:

$ alias | /usr/bin/which -i top10
top10='print -l  ${(o)history%% *} | uniq -c | sort -nr | head -n 10'
	/usr/bin/uniq
	/usr/bin/sort
	/usr/bin/head

It’s worth mentioning that GNU which with the -i option works for regular executables too:

$ alias | /usr/bin/which -i vim  
/usr/bin/vim

As we’ve seen, GNU which with the -i option is pretty general. However, the command is a bit too long and difficult to type. To solve this, we can create an alias to replace the shell built-in which:

alias which='alias | /usr/bin/which -i'

Now, we can simply type which to check executables, no matter if they’re regular commands or aliases:

$ which top10 
top10='print -l  ${(o)history%% *} | uniq -c | sort -nr | head -n 10'
	/usr/bin/uniq
	/usr/bin/sort
	/usr/bin/head

$ which vim  
/usr/bin/vim

$ which which
which='alias | /usr/bin/which -i'
	/usr/bin/which

5. Conclusion

In this article, we’ve learned two ways to get the full path of the commands in an alias: using type -P (-p in Zsh) and the GNU which command.

The GNU which command provides more functionalities than the built-in one. We’ve seen how to make GNU which replace the built-in one by creating an alias.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.