Detect if executable file is on user's PATH
BashBash Problem Overview
In a bash script, I need to determine whether an executable named foo
is on the PATH.
Bash Solutions
Solution 1 - Bash
You could also use the Bash builtin type -P
:
help type
cmd=ls
[[ $(type -P "$cmd") ]] && echo "$cmd is in PATH" ||
{ echo "$cmd is NOT in PATH" 1>&2; exit 1; }
Solution 2 - Bash
You can use which
:
path_to_executable=$(which name_of_executable)
if [ -x "$path_to_executable" ] ; then
echo "It's here: $path_to_executable"
fi
Solution 3 - Bash
TL;DR:
In bash
:
function is_bin_in_path {
builtin type -P "$1" &> /dev/null
}
Example usage of is_bin_in_path
:
% is_bin_in_path ls && echo "found in path" || echo "not in path"
found in path
In zsh
:
Use whence -p
instead.
For a version that works in both {ba,z}sh
:
# True if $1 is an executable in $PATH # Works in both {ba,z}sh function is_bin_in_path { if [[ -n $ZSH_VERSION ]]; then builtin whence -p "$1" &> /dev/null else # bash: builtin type -P "$1" &> /dev/null fi }
To test that ALL given commands are executables in $PATH:
# True iff all arguments are executable in $PATH
function is_bin_in_path {
if [[ -n $ZSH_VERSION ]]; then
builtin whence -p "$1" &> /dev/null
else # bash:
builtin type -P "$1" &> /dev/null
fi
[[ $? -ne 0 ]] && return 1
if [[ $# -gt 1 ]]; then
shift # We've just checked the first one
is_bin_in_path "$@"
fi
}
Example usage:
is_bin_in_path ssh-agent ssh-add && setup_ssh_agent
Non-solutions to avoid
This is not a short answer because the solution must correctly handle:
- Functions
- Aliases
- Builtin commands
- Reserved words
Examples which fail with plain type
(note the token after type
changes):
$ alias foo=ls
$ type foo && echo "in path" || echo "not in path"
foo is aliased to `ls'
in path
$ type type && echo "in path" || echo "not in path"
type is a shell builtin
in path
$ type if && echo "in path" || echo "not in path"
if is a shell keyword
in path
Note that in bash
, which
is not a shell builtin (it is in zsh
):
$ PATH=/bin
$ builtin type which
which is /bin/which
This answer says why to avoid using which
:
> Avoid which
. Not only is it an external process you're launching for doing very little (meaning builtins like hash
, type
or command
are way cheaper), you can also rely on the builtins to actually do what you want, while the effects of external commands can easily vary from system to system.
> Why care?
> - Many operating systems have a which
that doesn't even set an exit status, meaning the if which foo
won't even work there and will always report that foo
exists, even if it doesn't (note that some POSIX shells appear to do this for hash
too).
- Many operating systems make
which
do custom and evil stuff like change the output or even hook into the package manager.
command -v
In this case, also avoid The answer I just quoted from suggests using command -v
, however this doesn't apply to the current "is the executable in $PATH
?" scenario: it will fail in exactly the ways I've illustrated with plain type
above.
Correct solutions
In bash
we need to use type -P
:
> -P force a PATH search for each NAME, even if it is an alias, builtin, or function, and returns the name of the disk file that would be executed
In zsh
we need to use whence -p
:
> -p Do a path search for name even if it is an alias, reserved word, shell function or builtin.
Solution 4 - Bash
You can use the command
builtin, which is POSIX compatible:
if [ -x "$(command -v "$cmd")" ]; then
echo "$cmd is in \$PATH"
fi
The executable check is needed because command -v
detects functions and aliases as well as executables.
In Bash, you can also use type
with the -P
option, which forces a PATH
search:
if type -P "$cmd" &>/dev/null; then
echo "$cmd is in \$PATH"
fi
As already mentioned in the comments, avoid which
as it requires launching an external process and might give you incorrect output in some cases.
Solution 5 - Bash
if command -v foo ; then foo ; else echo "foo unavailable" ; fi
Solution 6 - Bash
Use which
$ which myprogram
Solution 7 - Bash
We can define a function for checking whether as executable exists by using which
:
function is_executable() {
which "$@" &> /dev/null
}
The function is called just like you would call an executable. "$@"
ensures that which
gets exactly the same arguments as are given to the function.
&> /dev/null
ensures that whatever is written to stdout or stderr by which
is redirected to the null device (which is a special device which discards the information written to it) and not written to stdout or stderr by the function.
Since the function doesn't explicitly return with an return code, when it does return, the exit code of the latest executed executable—which in this case is which
—will be the return code of the function. which
will exit with a code that indicates success if it is able to find the executable specified by the argument to the function, otherwise with an exit code that indicates failure. This behavior will automatically be replicated by is_executable
.
We can then use that function to conditionally do something:
if is_executable name_of_executable; then
echo "name_of_executable was found"
else
echo "name_of_executable was NOT found"
fi
Here, if
executes the command(s) written between it and then
—which in our case is is_executable name_of_executable
—and chooses the branch to execute based on the return code of the command(s).
Alternatively, we can skip defining the function and use which
directly in the if
-statement:
if which name_of_executable &> /dev/null; then
echo "name_of_executable was found"
else
echo "name_of_executable was NOT found"
fi
However, I think this makes the code slightly less readable.