Detect if executable file is on user's PATH [duplicate]

Solution 1:

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:

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:

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.

In this case, also avoid command -v

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:

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.