Categories
bash

How can I check if a program exists from a Bash script?

2797

How would I validate that a program exists, in a way that will either return an error and exit, or continue with the script?

It seems like it should be easy, but it’s been stumping me.

3

  • 1

    What is a “program”? Does it include functions and aliases? which returns true for these. type without arguments will additionally return true for reserved words and shell builtins. If “program” means “excutable in $PATH“, then see this answer.

    – Tom Hale

    Dec 16, 2018 at 2:12

  • 1

  • @TomHale It depends on which implementation of which you are using; which is not provided by Bash, but it is by e.g. Debian’s debianutils.

    – jarno

    Jan 18 at 15:28

3985

Answer

POSIX compatible:

command -v <the_command>

Example use:

if ! command -v <the_command> &> /dev/null
then
    echo "<the_command> could not be found"
    exit
fi

For Bash specific environments:

hash <the_command> # For regular commands. Or...
type <the_command> # To check built-ins and keywords

Explanation

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.

So, don’t use which. Instead use one of these:

$ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ type foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }
$ hash foo 2>/dev/null || { echo >&2 "I require foo but it's not installed.  Aborting."; exit 1; }

(Minor side-note: some will suggest 2>&- is the same 2>/dev/null but shorter – this is untrue. 2>&- closes FD 2 which causes an error in the program when it tries to write to stderr, which is very different from successfully writing to it and discarding the output (and dangerous!))

If your hash bang is /bin/sh then you should care about what POSIX says. type and hash‘s exit codes aren’t terribly well defined by POSIX, and hash is seen to exit successfully when the command doesn’t exist (haven’t seen this with type yet). command‘s exit status is well defined by POSIX, so that one is probably the safest to use.

If your script uses bash though, POSIX rules don’t really matter anymore and both type and hash become perfectly safe to use. type now has a -P to search just the PATH and hash has the side-effect that the command’s location will be hashed (for faster lookup next time you use it), which is usually a good thing since you probably check for its existence in order to actually use it.

As a simple example, here’s a function that runs gdate if it exists, otherwise date:

gnudate() {
    if hash gdate 2>/dev/null; then
        gdate "[email protected]"
    else
        date "[email protected]"
    fi
}

Alternative with a complete feature set

You can use scripts-common to reach your need.

To check if something is installed, you can do:

checkBin <the_command> || errorMessage "This tool requires <the_command>. Install it please, and then run this tool again."

3

  • Would you mind to explain what the &>/dev/null and >&2 parts are for? The line seems to work fine without them too. Thanks.

    – Geert

    Jul 16, 2010 at 5:54

  • 42

    @Geert: The &>/dev/null part hides the message ‘type’ emits when ‘foo’ doesn’t exist. The >&2 on the echo makes sure to send the error message to standard error instead of standard output; because that’s convention. They both appear on your terminal, but standard error is definitely the preferred output for error messages and unexpected warnings.

    – lhunath

    Jul 19, 2010 at 13:43

  • 148

    For those unfamiliar with ‘advanced’ i/o redirection in bash: 1) 2>&- (“close output file descriptor 2”, which is stderr) has the same result as 2> /dev/null; 2) >&2 is a shortcut for 1>&2, which you may recognize as “redirect stdout to stderr”. See the Advanced Bash Scripting Guide i/o redirection page for more info.

    Dec 21, 2011 at 19:48


750

The following is a portable way to check whether a command exists in $PATH and is executable:

[ -x "$(command -v foo)" ]

Example:

if ! [ -x "$(command -v git)" ]; then
  echo 'Error: git is not installed.' >&2
  exit 1
fi

The executable check is needed because bash returns a non-executable file if no executable file with that name is found in $PATH.

Also note that if a non-executable file with the same name as the executable exists earlier in $PATH, dash returns the former, even though the latter would be executed. This is a bug and is in violation of the POSIX standard. [Bug report] [Standard]

In addition, this will fail if the command you are looking for has been defined as an alias.

0

    226

    I agree with lhunath to discourage use of which, and his solution is perfectly valid for Bash users. However, to be more portable, command -v shall be used instead:

    $ command -v foo >/dev/null 2>&1 || { echo "I require foo but it's not installed.  Aborting." >&2; exit 1; }
    

    Command command is POSIX compliant. See here for its specification: command – execute a simple command

    Note: type is POSIX compliant, but type -P is not.

    6

    • 3

      Same as above – exit 1; kills an xterm, if invoked from there.

      Feb 18, 2012 at 17:14

    • 1

      This wouldn’t work on a standard sh: you &> isn’t a valid redirect instructions.

      – jyavenard

      Mar 4, 2012 at 11:19

    • 7

      @jyavenard: The question is tagged bash, hence the more concise bash-specific redirect notation &>/dev/null. However, I agree with you, what really matters is portability, I’ve edited my answer accordingly, now using standard sh redirect >/dev/null 2>&1.

      – GregV

      Mar 5, 2012 at 10:58

    • to even improve more this answer I would do two things: 1: use “&>” to simplify it, like Josh’s answer. 2: break the { } into an extra line, putting a tab before the echo, for readability

      – knocte

      May 21, 2016 at 13:06

    • 1

      Fails on builtins and reserved words: Try this with the word then for instance. See this answer if you require the executable to exist in $PATH.

      – Tom Hale

      Dec 16, 2018 at 2:13