60

In a bash script, I need to determine whether an executable named foo is on the PATH.

4
  • 8
    This is a duplicate of stackoverflow.com/a/677212, which has much more complete answers. Commented May 30, 2012 at 15:26
  • 1
    ........ why is this feature not in posix Commented Nov 3, 2016 at 19:44
  • 1
    That existing question is not asking if the executable is on the users PATH.. it just asks if the program exists? So, I find this question. Not the "duplicate". Commented Dec 11, 2019 at 23:32
  • Just use builtin hash for the job. Commented Jan 18, 2022 at 19:41

7 Answers 7

56

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; }
Sign up to request clarification or add additional context in comments.

6 Comments

can simplify: type -P "$cmd" && echo "in path" || echo "not in path"
Since he didn't specify bash, better to avoid bashisms stackoverflow.com/a/677212
Bash was most certainly specified in the question: "In a bash script…"
Out of curiosity, why not just do this: type -P " $cmd" > /dev/null && echo "yay" or if type -P "$cmd" > /dev/null; then instead of using a subshell and wrapping the command in [[ ]]?
@Hubro you're correct on a wasted fork() and test. See this answer for an explanation of -P and a more efficient, reusable function which works in both bash and zsh.
|
45

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

6 Comments

The check for executable mode flag is redundant. which only returns executable files (including scripts)
The point of the executable check is to make sure that the result of which was the executable, and not an error message.
Ok, I get it. if [ -n "$path_to_executable" ] would have made that clearer (and more efficient)
which should be avoided, see stackoverflow.com/a/677212
which is actually a bad choice. Rather use the builtin alternative. (See trevor's answer) Also, this question is a duplicate of stackoverflow.com/questions/592620/…
|
32

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.

5 Comments

"It works correctly for aliases and functions" - not true. Try this in Bash: alias foo=ls; is_bin_in_path foo && echo "in path" || echo "not in path".
Good catch, @EugeneYarmash. Updated and fixed.
type -P is a bashism, and will cause problems when used from scripts where you can't guarantee that the shell is bash --- for example, makefiles with $(shell).
@David Given, the question is tagged bash....
@TomHale So it is --- my bad!
17

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.

2 Comments

Its better then which as is doesnt throw an error
I don't see why -x is absolutely necessary. command already checks if it is something executable as a command (like an alias or function). you'd only need -x if it absolutely must be an executable file and nothing else, which seems more strict
1

if command -v foo ; then foo ; else echo "foo unavailable" ; fi

2 Comments

This will break with functions or aliases. This answer works, in both bash and zsh
Can you add some more explanation to that snippet?
0

Use which

$ which myprogram

2 Comments

which is indeed, awesome =)
which should be avoided, see stackoverflow.com/a/677212
0

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.

4 Comments

@RamGhadiyaram Sure, I can put in some comments. What is "Low Qualtiy posts in SO"?
these are the review queues 3rd one from left menu. if they feel answer has minimal information and not explained then it will go to review queue. to close. most commonly code only answers will go. in your case it is.
@RamGhadiyaram Is this good?
should NOT use which in a bash shell. see : unix.stackexchange.com/questions/85249/…

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.