By gregh


2009-02-26 21:52:49 8 Comments

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.

30 comments

@Autodidact 2019-10-25 17:26:24

Assuming you are already following safe shell practices:

set -eu -o pipefail
shopt -s failglob

./dummy --version 2>&1 >/dev/null

This assumes the command can be invoked in such a way that it does (almost) nothing, like reporting its version or showing help.

If the dummy command is not found, Bash exits with the following error...

./my-script: line 8: dummy: command not found

This is more useful and less verbose than the other command -v (and similar) answers because the error message is auto generated and also contains a relevant line number.

@Palec 2019-10-28 06:58:24

This answer is very unclear to those who are not good with shell as it does not state how this code is supposed to be used and it differs from many other answers in that the failed branch of the test for dummy's existence is out of hands of the script's author. Also, it lacks explanation of how it works and that it changes settings of the execution environment.

@Palec 2019-10-28 06:59:58

This is a Bash question, but it might be worth mentioning that the non-POSIX shopt call can be replaced with POSIX set -f, which is shorter and portable. The pipefail option is not supported in POSIX, though, and there is no alternative, AFAIK.

@Farhad Sakhaei 2019-07-11 06:02:54

Use:

if [[ `command --help` ]]; then
  echo "This command exists"
else
  echo "This command does not exist";
fi

Put in a working switch, such as "--help" or "-v" in the if check: if [[ command --help ]]; then

@Klevin Kona 2018-11-27 16:03:15

This will tell according to the location if the program exist or not:

if [ -x /usr/bin/yum ]; then
    echo This is Centos
fi

@Klevin Kona 2018-11-29 14:11:49

Yes i added this command if you need to install a package in the sevrer, Open suse, centos, Debian

@Peter Mortensen 2020-01-16 14:24:06

The syntax highlighting is off in the "echo" line. What is the solution? Does it suggest the Bash script should be different?

@fjk 2018-08-10 08:30:41

I would just try and call the program with for example --version or --help and check if the command succeeded or failed

Used with set -e, the script will exit if the program is not found, and you will get a meaningful error message:

#!/bin/bash
set -e
git --version >> /dev/null

@AnthonyC 2018-06-25 16:53:46

Command -v works fine if the POSIX_BUILTINS option is set for the <command> to test for, but it can fail if not. (It has worked for me for years, but I recently ran into one where it didn't work.)

I find the following to be more failproof:

test -x $(which <command>)

Since it tests for three things: path, existence and execution permission.

@algal 2019-02-20 02:26:23

Doesn't work. test -x $(which ls) returns 0, as does test -x $(which sudo), even though ls is installed and runnable and sudo is not even installed within the docker container I'm running in.

@JoniVR 2019-04-02 12:19:10

@algal You need to use quotes I think, so test -x "$(which <command>)"

@AnthonyC 2019-04-02 15:38:36

@algal Perhaps ls is aliased? I dont think it would work if the command has parameter.

@keisar 2018-06-19 12:02:30

There are a ton of options here, but I was surprised no quick one-liners. This is what I used at the beginning of my scripts:

[[ "$(command -v mvn)" ]] || { echo "mvn is not installed" 1>&2 ; exit 1; }
[[ "$(command -v java)" ]] || { echo "java is not installed" 1>&2 ; exit 1; }

This is based on the selected answer here and another source.

@rpr 2016-06-18 23:19:53

In case you want to check if a program exists and is really a program, not a Bash built-in command, then command, type and hash are not appropriate for testing as they all return 0 exit status for built-in commands.

For example, there is the time program which offers more features than the time built-in command. To check if the program exists, I would suggest using which as in the following example:

# First check if the time program exists
timeProg=`which time`
if [ "$timeProg" = "" ]
then
  echo "The time program does not exist on this system."
  exit 1
fi

# Invoke the time program
$timeProg --quiet -o result.txt -f "%S %U + p" du -sk ~
echo "Total CPU time: `dc -f result.txt` seconds"
rm result.txt

@nodakai 2016-03-10 02:31:54

I'd say there isn't any portable and 100% reliable way due to dangling aliases. For example:

alias john='ls --color'
alias paul='george -F'
alias george='ls -h'
alias ringo=/

Of course, only the last one is problematic (no offence to Ringo!). But all of them are valid aliases from the point of view of command -v.

In order to reject dangling ones like ringo, we have to parse the output of the shell built-in alias command and recurse into them (command -v isn't a superior to alias here.) There isn't any portable solution for it, and even a Bash-specific solution is rather tedious.

Note that a solution like this will unconditionally reject alias ls='ls -F':

test() { command -v $1 | grep -qv alias }

@Basil Musa 2016-03-23 16:08:18

Good point. However, when run from inside a bash script, aliases are not visible.

@Basil Musa 2016-03-23 16:12:20

There's also a problem, it will return false when the command 'alias' is checked. When it should return true. Example: test "alias"

@dragon788 2017-07-02 03:07:47

I just tested and using shopt -u expand_aliases ignores/hides these aliases and shopt -s expand_aliases shows them via command -v.

@A.N 2015-09-22 00:41:46

I use this, because it's very easy:

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then echo exists;else echo "not exists";fi

or

if [ `LANG=C type example 2>/dev/null|wc -l` = 1 ];then
echo exists
else echo "not exists"
fi

It uses shell builtins and programs' echo status to standard output and nothing to standard error. On the other hand, if a command is not found, it echos status only to standard error.

@user619271 2015-08-26 09:23:12

If you guys/gals can't get the things in answers here to work and are pulling hair out of your back, try to run the same command using bash -c. Just look at this somnambular delirium. This is what really happening when you run $(sub-command):

First. It can give you completely different output.

$ command -v ls
alias ls='ls --color=auto'
$ bash -c "command -v ls"
/bin/ls

Second. It can give you no output at all.

$ command -v nvm
nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found

@Palec 2015-08-26 10:47:22

The differences are caused by the difference between interactive and non-interactive mode of the shell. Your ~/.bashrc is read only when the shell is non-login and interactive. The second one looks odd though, because this must be caused by a difference in PATH environment variable, but subshells inherit the environment.

@user619271 2015-08-26 12:16:47

In my case .bashrc have a [ -z "$PS1" ] && return prepended by # If not running interactively, don't do anything so I guess that is a reason why even explicit sourcing of bashrc in non-interactive mode doesn't help. The problem can be workarounded by calling a script with a ss64.com/bash/source.html dot operator . ./script.sh but that is not a thing one would like to remember to type each time.

@Palec 2015-08-26 13:12:10

Sourcing scripts that are not supposed to be sourced is a bad idea. All I was trying to say is that your answer has little to do with the question being asked and much to do with Bash and its (non-)interactive mode.

@Palec 2015-08-26 16:06:26

If it explained what is going on in these cases, it would be a helpful addendum to an answer.

@ThCTLo 2015-03-27 14:26:27

My setup for a Debian server:

I had the problem when multiple packages contained the same name.

For example apache2. So this was my solution:

function _apt_install() {
    apt-get install -y $1 > /dev/null
}

function _apt_install_norecommends() {
    apt-get install -y --no-install-recommends $1 > /dev/null
}
function _apt_available() {
    if [ `apt-cache search $1 | grep -o "$1" | uniq | wc -l` = "1" ]; then
        echo "Package is available : $1"
        PACKAGE_INSTALL="1"
    else
        echo "Package $1 is NOT available for install"
        echo  "We can not continue without this package..."
        echo  "Exitting now.."
        exit 0
    fi
}
function _package_install {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install $1
            sleep 0.5
        fi
    fi
}

function _package_install_no_recommends {
    _apt_available $1
    if [ "${PACKAGE_INSTALL}" = "1" ]; then
        if [ "$(dpkg-query -l $1 | tail -n1 | cut -c1-2)" = "ii" ]; then
             echo  "package is already_installed: $1"
        else
            echo  "installing package : $1, please wait.."
            _apt_install_norecommends $1
            sleep 0.5
        fi
    fi
}

@freno 2014-11-20 16:55:05

If there isn't any external type command available (as taken for granted here), we can use POSIX compliant env -i sh -c 'type cmd 1>/dev/null 2>&1':

# Portable version of Bash's type -P cmd (without output on stdout)
typep() {
   command -p env -i PATH="$PATH" sh -c '
      export LC_ALL=C LANG=C
      cmd="$1"
      cmd="`type "$cmd" 2>/dev/null || { echo "error: command $cmd not found; exiting ..." 1>&2; exit 1; }`"
      [ $? != 0 ] && exit 1
      case "$cmd" in
        *\ /*) exit 0;;
            *) printf "%s\n" "error: $cmd" 1>&2; exit 1;;
      esac
   ' _ "$1" || exit 1
}

# Get your standard $PATH value
#PATH="$(command -p getconf PATH)"
typep ls
typep builtin
typep ls-temp

At least on Mac OS X v10.6.8 (Snow Leopard) using Bash 4.2.24(2) command -v ls does not match a moved /bin/ls-temp.

@blueyed 2014-07-21 00:13:17

hash foo 2>/dev/null: works with Z shell (Zsh), Bash, Dash and ash.

type -p foo: it appears to work with Z shell, Bash and ash (BusyBox), but not Dash (it interprets -p as an argument).

command -v foo: works with Z shell, Bash, Dash, but not ash (BusyBox) (-ash: command: not found).

Also note that builtin is not available with ash and Dash.

@Ciro Santilli 新疆改造中心法轮功六四事件 2014-03-23 10:09:29

Check for multiple dependencies and inform status to end users

for cmd in latex pandoc; do
  printf '%-10s' "$cmd"
  if hash "$cmd" 2>/dev/null; then
    echo OK
  else
    echo missing
  fi
done

Sample output:

latex     OK
pandoc    missing

Adjust the 10 to the maximum command length. It is not automatic, because I don't see a non-verbose POSIX way to do it: How can I align the columns of a space separated table in Bash?

Check if some apt packages are installed with dpkg -s and install them otherwise.

See: Check if an apt-get package is installed and then install it if it's not on Linux

It was previously mentioned at: How can I check if a program exists from a Bash script?

@Patrice Levesque 2015-02-22 18:15:06

Non-verbose way to do it: 1) get rid of the width specifier; 2) add a space after your command name's printf; 3) pipe your for loop to column -t (part of util-linux).

@dcharles 2011-06-24 17:01:08

To use hash, as @lhunath suggests, in a Bash script:

hash foo &> /dev/null
if [ $? -eq 1 ]; then
    echo >&2 "foo not found."
fi

This script runs hash and then checks if the exit code of the most recent command, the value stored in $?, is equal to 1. If hash doesn't find foo, the exit code will be 1. If foo is present, the exit code will be 0.

&> /dev/null redirects standard error and standard output from hash so that it doesn't appear onscreen and echo >&2 writes the message to standard error.

@Beni Cherniavsky-Paskin 2012-11-15 12:30:26

Why not just if hash foo &> /dev/null; then ... ?

@tim 2011-06-01 17:49:22

To mimic Bash's type -P cmd, we can use the POSIX compliant env -i type cmd 1>/dev/null 2>&1.

man env
# "The option '-i' causes env to completely ignore the environment it inherits."
# In other words, there are no aliases or functions to be looked up by the type command.

ls() { echo 'Hello, world!'; }

ls
type ls
env -i type ls

cmd=ls
cmd=lsx
env -i type $cmd 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }

@Adrian Frühwirth 2014-04-17 09:00:57

Why is this being upvoted? On which systems does this actually work for you? type seems to be a builtin in most shells so this can't work because env uses execvp to run command so command cannot be a builtin (and the builtin will always be run within the same environment). This fails for me in bash, ksh93, zsh, busybox [a]sh and dash all of which provide type as a shell builtin.

@Greg K 2011-04-08 15:30:28

I had to check if Git was installed as part of deploying our CI server. My final Bash script was as follows (Ubuntu server):

if ! builtin type -p git &>/dev/null; then
  sudo apt-get -y install git-core
fi

@tripleee 2011-09-03 08:52:20

The conditional is rather useless, modulo the startup time to run apt-get, as apt-get will be satisfied and exit if git-core is already installed.

@Beni Cherniavsky-Paskin 2012-11-15 12:33:45

Its startup time is non-negligible, but the more important motivation is sudo: without the conditional, it would always stop and ask for password (unless you did a sudo recently). BTW, it may be useful to do sudo -p "Type your password to install missing git-core: " so the prompt doesn't come out of the blue.

@GregV 2011-01-24 18:16:07

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.

@user unknown 2012-02-18 17:14:02

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

@jyavenard 2012-03-04 11:19:47

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

@GregV 2012-03-05 10:58:08

@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.

@knocte 2016-05-21 13:06:34

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

@equant 2017-09-29 19:26:30

I just put this one liner into a bash function if anyone wants it... github.com/equant/my_bash_tools/blob/master/tarp.bash

@Tom Hale 2018-12-16 02:13:16

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.

@Nathan Crause 2010-01-15 23:16:17

For those interested, none of the methodologies in previous answers work if you wish to detect an installed library. I imagine you are left either with physically checking the path (potentially for header files and such), or something like this (if you are on a Debian-based distribution):

dpkg --status libdb-dev | grep -q not-installed

if [ $? -eq 0 ]; then
    apt-get install libdb-dev
fi

As you can see from the above, a "0" answer from the query means the package is not installed. This is a function of "grep" - a "0" means a match was found, a "1" means no match was found.

@tripleee 2013-10-21 07:04:33

However, the anti-pattern cmd; if [ $? -eq 0 ]; then should be refactored to if cmd; then

@Weijun Zhou 2018-11-21 19:35:07

This only works for libs installed via dpkg or apt

@Magnus 2009-07-11 18:38:17

I never did get the previous answers to work on the box I have access to. For one, type has been installed (doing what more does). So the builtin directive is needed. This command works for me:

if [ `builtin type -p vim` ]; then echo "TRUE"; else echo "FALSE"; fi

@nyuszika7h 2017-02-02 14:42:03

The brackets are not part of the if syntax, simply use if builtin type -p vim; then .... And the backticks are really ancient and deprecated syntax, $() is supported even by sh on all modern systems.

@bin 2009-03-24 11:23:20

Use Bash builtins if you can:

which programname

...

type -P programname

@tripleee 2013-10-21 07:01:17

Huh? which is not a Bash builtin.

@RobertG 2016-07-25 16:30:10

type -P programname is to be preferred, see accepted answer

@mikemaccana 2018-01-02 18:24:30

@RobertG All I see is that -P isn't POSIX. Why is type -P preferred?

@RobertG 2018-01-05 10:06:50

I should have phrased that "to be preferred in bash environments" - as I intentded to answer the bash-specific previous comment. Anyhow, that was years ago - I guess I should just, again, point you to the answer marked as "accpeted"

@dreamlax 2009-02-26 22:01:52

It depends on whether you want to know whether it exists in one of the directories in the $PATH variable or whether you know the absolute location of it. If you want to know if it is in the $PATH variable, use

if which programname >/dev/null; then
    echo exists
else
    echo does not exist
fi

otherwise use

if [ -x /path/to/programname ]; then
    echo exists
else
    echo does not exist
fi

The redirection to /dev/null/ in the first example suppresses the output of the which program.

@lhunath 2009-03-24 14:53:47

You really shouldn't be using "which" for the reasons outlined in my comment.

@Adam Davis 2009-02-26 21:56:53

The which command might be useful. man which

It returns 0 if the executable is found and returns 1 if it's not found or not executable:

NAME

       which - locate a command

SYNOPSIS

       which [-a] filename ...

DESCRIPTION

       which returns the pathnames of the files which would
       be executed in the current environment, had its
       arguments been given as commands in a strictly
       POSIX-conformant shell. It does this by searching
       the PATH for executable files matching the names
       of the arguments.

OPTIONS

       -a     print all matching pathnames of each argument

EXIT STATUS

       0      if all specified commands are 
              found and executable

       1      if one or more specified commands is nonexistent
              or not executable

       2      if an invalid option is specified

The nice thing about which is that it figures out if the executable is available in the environment that which is run in - it saves a few problems...

@dmckee --- ex-moderator kitten 2009-02-26 22:01:01

Use which if you looking for any executable named foo, but see my answer if you want to check a particular file /path/to/a/named/foo. Also note that which may not be available on some minimal systems, though it should be present on any full fledged installation...

@lhunath 2009-03-24 12:46:54

Don't rely in the exit status of which. Many operating systems have a which that doesn't even set an exit status other than 0.

@dmckee --- ex-moderator kitten 2009-02-26 21:57:18

Try using:

test -x filename

or

[ -x filename ]

From the Bash manpage under Conditional Expressions:

 -x file
          True if file exists and is executable.

@lhunath 2009-03-24 12:45:56

That means you need to already know the full path to the application.

@dmckee --- ex-moderator kitten 2009-03-24 16:42:06

The OP didn't specify if he wanted to check for a specific instance or for any executable instance...I answered it the way I read it.

@lhunath 2009-03-24 12:45:20

Answer

POSIX compatible:

command -v <the_command>

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
}

@Geert 2010-07-16 05:54:30

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

@lhunath 2010-07-19 13:43:45

@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.

@momeara 2011-04-01 19:18:42

the -P flag does not work in 'sh', eg stackoverflow.com/questions/2608688/…

@mikewaters 2011-12-21 19:48:19

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.

@rogeriopvl 2012-03-06 20:11:23

Btw, there are allot of programs that don't support "-v" (git for instance), it's a safer bet to use "--version".

@michuelnik 2012-08-30 08:53:26

If command is safest to use with the POSIX constraint, why should I use type or hash even if I was using bash?

@lhunath 2012-08-31 09:08:57

@michuelnik I explain how hash causes the command name to be added to the hash table and type has options to do things command can't. All-in-all it's a matter of preference, the type and hash solutions are a bit shorter. Also, don't confuse bash and sh -- there is zero gain from using POSIX syntax when your interpreter is bash. You gain no portability at all. Your hashbang is bash and you will always require it. You might as well use normal bash syntax. Just like it makes little sense to limit yourself to C syntax when you're compiling with a C++ compiler anyway.

@don bright 2013-06-09 03:04:08

hash doesnt work on minix's default shell. command -v doesnt work on minix's default shell. (minix 3)

@lhunath 2013-06-10 13:26:53

@mikewaters I don't think it's very responsible to link to LDP's ABS. It is broadly accepted by bash experts to be a bad influence for bash students. Instead, I would propose the following to learn about I/O in bash: mywiki.wooledge.org/BashSheet#Streams

@lhunath 2013-06-12 20:00:38

@mikewaters The ABS looks fairly advanced and describes a wide range of bash and non-bash CLI functionality, but it is very negligent in many aspects and does not follow good practice. I don't have nearly enough space in this comment to do a write-up; but I can paste a few random examples of BAD code: while read element ; do .. done <<< $(echo ${ArrayVar[*]}), for word in $(fgrep -l $ORIGINAL *.txt), ls -l "$directory" | sed 1d , {{for a in seq $BEGIN $END}}, ... Many have tried to contact the authors and propose improvements but it's no wiki and requests have landed on deaf ears.

@mikewaters 2013-06-13 14:25:50

@lhunath thanks. I tried to edit my comment no luck - too old. catonmat has a good pdf at catonmat.net/download/bash-redirections-cheat-sheet.pdf

@blong 2014-05-20 18:53:54

Can someone explain to me in very simple terms the purpose of the curly braces to print the failure message in this construct: $ command -v foo >/dev/null 2>&1 || { echo >&2 "I require foo but it's not installed. Aborting."; exit 1; } ? I ask because at least in the simple case, this will print the error message: $ command -v foo >/dev/null 2>&1 || echo "I require foo but it's not installed. Aborting.". Is the curly braces helpful for printing to STDERR?

@lhunath 2014-05-21 02:47:47

@CMCDragonkai In that case, you normally wouldn't do a test at all and just run the command. You can prefix a command with ! (and a space) to negate its exit status, or you can replace || with &&. See man bash or mywiki.wooledge.org/BashGuide/…

@lhunath 2014-05-21 02:50:10

@b.long The curly braces serve to combine the echo and exit commands so that they're both run as a result of command's exit code. If you leave out the curly braces, only the echo command will run conditionally and the exit command will always run, whether the command exists or not. See mywiki.wooledge.org/BashGuide/…

@David Da Silva Contín 2014-07-03 10:29:10

command on fish shell doesn't support the -v option.

@lhunath 2014-07-04 12:56:21

@DavidDaSilvaContín Useful to know, though note that fish is not a POSIX shell either. command -v also doesn't work in cmd.exe.

@user2350426 2014-08-05 06:31:44

Note that: "command -v" fails on aliases.And "which" works: command -v ls ==> alias ls='ls --group-directories-first'. And: which ls ==> /bin/ls.

@lhunath 2014-08-06 20:37:42

@bize you've shown that "which" does NOT work. since it INCORRECTLY tells you /bin/ls which is NOT what the ls command on the prompt will invoke. it will invoke the ls --group-directories first command. so command -v reports correctly. Consider alias cp=ls, now does command -v or which report the correct thing?

@user2350426 2014-08-08 14:46:08

@lhunah the title of this page asks: "How to check if a program exists from a bash script?", the only real way to do so is to test for existence of a file, something like: [ -e /bin/ls ], so: you need the correct location of the file not an abstract word (ls). Much less the additional options. Besides, if you change the ls program to something like /bin/ls-temp, the "command -v ls" still reports the same, while "which" does indeed reports "null" (empty).

@nyuszika7h 2014-11-05 14:36:28

@mikewaters 2>&- is not the same as 2>/dev/null. The former closes the file descriptor, while the latter simply redirects it to /dev/null. You may not see an error because the program tries to inform you on stderr that stderr is closed.

@Lone Learner 2016-01-03 03:33:28

None of type, command -v or hash are guaranteed to be present in a POSIX.1-2004 compliant shell. See stackoverflow.com/q/34572700/1175080 and stackoverflow.com/q/34572886/1175080.

@kojiro 2016-01-25 13:39:40

It might be worth noting that which foo, if it exists, is likely to yield failure if foo is a shell alias, builtin or function, whereas the above POSIX alternatives woulds yield success. Thus e.g. which foo && foo is closer to just plain command foo than command -v foo >/dev/null 2>&1 && foo.

@jarno 2016-01-31 12:10:08

Do you have a specific example about what "custom and evil suff" which may do? Would it give wrong answer or just be less efficient than a buit-in command? How would it change the output?

@user137369 2016-03-02 13:36:46

Why >/dev/null 2>&1 and not simply &>/dev/null?

@Urhixidur 2016-06-09 15:38:18

Whereas the 'hash test' seems to usually work, I've run into one case where it does not. I'm testing for the groupadd command (used by CentOS, for instance, whereas Ubuntu uses addgroup). This command lives in /usr/sbin and can only be invoked as sudo. $ groupadd --version yields bash: /usr/sbin/groupadd: Permission denied, $ hash groupadd yields bash: hash: groupadd: not found,and $ sudo hash groupadd yields sudo: hash: command not found. How to fix this?

@lhunath 2016-06-10 16:19:47

@Urhixidur Note that hash will only look in PATH. If your user's PATH does not include sbin, hash will not find the binary that lives there. If you want to run bash code with sudo, you need to invoke bash from sudo: if sudo bash -c 'hash groupadd'; then ...

@xuhdev 2016-10-11 06:58:30

I have an efficiency comparison of all these approaches: topbug.net/blog/2016/10/11/…

@Mike Q 2017-08-14 20:14:42

you need ';' inside of the { } like : { echo "failed"; }

@uchuugaka 2017-08-16 00:42:18

It's wrong to say avoid which, unless you mean avoid it for portability. If you know you are running always on an OS that it works, then it works without all the hacky solutions we have to list up.

@uchuugaka 2017-08-16 00:43:08

Not to say these are bad solutions, but which works perfectly well on macOS for this use case to say "is this visible in the current PATH?"

@lhunath 2017-08-17 01:43:29

The alternatives are not hacks. The alternatives are 100% good and portable, there is not a single advantage gained by which and there are many downsides. which does NOT in fact tell you what your shell will do when you run a command. which is a lie. Only your shell can tell you what a command name resolves to in that shell. Only a shell builtin can provide the information you need. Stop being the poison that is spreading misinformation.

@mkobit 2017-10-11 16:06:30

Where do I find the docs for command? Googling for it doesn't seem to be working for me.

@lhunath 2017-10-12 17:35:07

@mkobit The documentation for Bash is in man bash. A short summary can be obtained using help command

@Evan Carroll 2017-11-13 03:04:23

How come you don't even mention test -x which is in POSIX 2008.

@lhunath 2017-11-14 03:30:10

@EvanCarroll test -x can only tell you whether a file is executable. That requires that you already know the exact path to the file. If you have full knowledge of the one and only path to the file of your program, you could use test, but it's not very useful in answering the question "can I execute a command that will run a program", for that, you need to perform a command name resolution or PATH search, which test does not do.

@Piyush Soni 2017-12-19 06:38:51

The only problem with this approach is, on a lot of systems "command" itself is not found (like on mine - there is no type OR command OR hash commands already present). So how do you check the existence of a 'command' which is used to test the existence of other commands?

@lhunath 2017-12-20 20:35:44

Please note that command and type and hash are not external programs. They are shell built-ins. They resolve the program that the SHELL itself would run, using the internal shell logic, which is exactly what you want. Please clarify what you mean with "a lot of systems "command" itself is not found", because that's just a very misleading statement to make for the readers here.

@calandoa 2018-04-12 14:15:36

Actually, I just find out one pro for which against c/t/h with Ubuntu and GNU make. This last tool has a small bug because of an optimization and will not always execute builtins. Because which is an executable on Ubuntu, $(shell which cmd) will work and $(shell command -v cmd) will not. Same thing for execution lines in rules.

@Jeroen Bouman 2018-10-18 09:31:46

Very nice, command -v is also applicable on Mac OS X Darwin

@Housni 2018-10-24 17:29:12

Great answer! The only thing I can add is perhaps exiting with an exit code of 127 instead of the catchall 1 exit code. See: tldp.org/LDP/abs/html/exitcodes.html

@Harold Fischer 2019-02-14 04:54:03

@lhunath In your example command -v foo >/dev/null 2>&1 ..., is there any reason you are redirecting stderr to stdout? command -v foo does not send anything to stderr, regardless of whether or not foo is a command.

@Brian Chrisman 2019-08-19 18:38:34

Note: despite the 'command' label in 'command -v', and the usage of 'command <executable> <args...>' invoking an actual executable (and not a bash function or alias), 'command -v' reports bash functions as well as actual 'command's.

@joeytwiddle 2019-10-16 07:15:05

@HaroldFischer I also wondered. I suppose command -v might write to stderr if it encounters a serious error, for example an I/O error with one of the PATHs. It will definitely error if you pass -foo instead of foo, although that can be avoided with command -v -- -foo

@nyuszika7h 2014-11-05 14:33:50

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.

@uchuugaka 2017-08-16 00:38:41

unfortunately this fails on older bash 3.2

@einpoklum 2017-10-26 10:14:10

Will command -v produce a path even for a non-executable file? That is, the -x really necessary?

@Ken Sharp 2017-10-26 13:03:00

@einpoklum -x tests that the file is executable, which is what the question was.

@einpoklum 2017-10-26 13:04:27

@KenSharp: But that seems to be redundant, since command will itself test for its being executable - won't it?

@nyuszika7h 2017-10-26 13:52:42

@einpoklum Yes, it is necessary. In fact, even this solution may break in one edge case. Thanks for bringing this to my attention. dash, bash, and zsh all skip over non-executable files in $PATH when executing a command. However, the behavior of command -v is very inconsistent. In dash, it returns the first matching file in $PATH, regardless of whether it's executable or not. In bash, it returns the first executable match in $PATH, but if there's none, it can return a non-executable file. And in zsh, it will never return a non-executable file.

@nyuszika7h 2017-10-26 13:55:52

And this is true even in the shells' POSIX sh emulation mode.

@nyuszika7h 2017-10-26 14:12:11

As far as I can tell, dash is the only one out of those three that's non-POSIX-compliant; [ -x "$(command -v COMMANDNAME)"] will work in the other two. Looks like this bug has already been reported but hasn't got any responses yet: bugs.debian.org/cgi-bin/bugreport.cgi?bug=874264

@Evan Carroll 2017-11-13 03:10:56

@nyuszika7h I'm using bash, and I don't see it returning a non-executable file. unix.stackexchange.com/q/404144/3285

@Luiz Berti 2018-12-24 17:07:05

@nyuszika7h it's important to note that if the command you wanna check for is not an executable, but a shell function or builtin, this solution will break. It should be used exclusively to check for executables.

@ManuelTS 2019-05-22 08:08:27

Nice snipped allowing to check if a program is installed or not on one line with a basic please install reaction: [[ ! -x "$(command -v perl)" ]] && echo "perl required, install with \"sudo apt install -y perl\"" && exit 1.

@Eduardo Lucio 2019-05-22 13:22:14

Futher information about "-x" on "if" statement: askubuntu.com/a/445473/134723 =D

@ecwpz91 2017-09-06 00:42:27

Script

#!/bin/bash

# Commands found in the hash table are checked for existence before being
# executed and non-existence forces a normal PATH search.
shopt -s checkhash

function exists() {
 local mycomm=$1; shift || return 1

 hash $mycomm 2>/dev/null || \
 printf "\xe2\x9c\x98 [ABRT]: $mycomm: command does not exist\n"; return 1;
}
readonly -f exists

exists notacmd
exists bash
hash
bash -c 'printf "Fin.\n"'

Result

✘ [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
Fin.

@Da'Jour 2017-04-16 01:21:00

GIT=/usr/bin/git                     # STORE THE RELATIVE PATH
# GIT=$(which git)                   # USE THIS COMMAND TO SEARCH FOR THE RELATIVE PATH

if [[ ! -e $GIT ]]; then             # CHECK IF THE FILE EXISTS
    echo "PROGRAM DOES NOT EXIST."
    exit 1                           # EXIT THE PROGRAM IF IT DOES NOT
fi

# DO SOMETHING ...

exit 0                               # EXIT THE PROGRAM IF IT DOES

@Arya McCarthy 2017-06-19 15:06:38

1) This is an absolute path. 2) You only check whether the program exists in a particular location, rather than being callable. I could have something in /usr/local/bin instead and your code would exit.

@Romário 2015-12-07 21:17:24

Expanding on @lhunath's and @GregV's answers, here's the code for the people who want to easily put that check inside an if statement:

exists()
{
  command -v "$1" >/dev/null 2>&1
}

Here's how to use it:

if exists bash; then
  echo 'Bash exists!'
else
  echo 'Your system does not have Bash'
fi

@Palec 2015-12-12 09:23:19

Willingness to learn and improve must be rewarded. +1 This is clean and simple. The only thing I can add is that command succeeds even for aliases, which might be somewhat counterintuitive. Checking for existence in an interactive shell will give different results from when you move it to a script.

@dragon788 2017-07-02 03:13:17

I just tested and using shopt -u expand_aliases ignores/hides aliases (like the alias ls='ls -F' mentioned in another answer) and shopt -s expand_aliases resolves them via command -v. So perhaps it should be set prior to the check and unset after, though it could affect the function return value if you don't capture and return the output of the command call explicitly.

@Jan 2010-01-20 13:43:17

I couldn't get one of the solutions to work, but after editing it a little I came up with this. Which works for me:

dpkg --get-selections | grep -q linux-headers-$(uname -r)

if [ $? -eq 1 ]; then
        apt-get install linux-headers-$(uname -r)
fi

@Daniel W. 2016-07-11 10:08:45

This only works for Debian based systems like Ubuntu.

@Weijun Zhou 2018-11-21 19:51:04

Even for those systems it can fail if the command is not installed via dpkg or apt.

Related Questions

Sponsored Content

10 Answered Questions

[SOLVED] Check existence of input argument in a Bash shell script

  • 2011-06-26 05:49:21
  • user775187
  • 1162073 View
  • 1256 Score
  • 10 Answer
  • Tags:   bash shell

14 Answered Questions

[SOLVED] Make a Bash alias that takes a parameter?

  • 2011-08-20 12:11:41
  • Hello
  • 490996 View
  • 1196 Score
  • 14 Answer
  • Tags:   bash alias

65 Answered Questions

[SOLVED] How to get the source directory of a Bash script from within the script itself

  • 2008-09-12 20:39:56
  • Jiaaro
  • 1616997 View
  • 4750 Score
  • 65 Answer
  • Tags:   bash directory

32 Answered Questions

[SOLVED] How do I split a string on a delimiter in Bash?

30 Answered Questions

[SOLVED] How to concatenate string variables in Bash

23 Answered Questions

[SOLVED] How to check if a string contains a substring in Bash

  • 2008-10-23 12:37:31
  • davidsheldon
  • 1945488 View
  • 2411 Score
  • 23 Answer
  • Tags:   string bash substring

32 Answered Questions

[SOLVED] How to check if a variable is set in Bash?

  • 2010-08-30 14:54:38
  • prosseek
  • 1225332 View
  • 1460 Score
  • 32 Answer
  • Tags:   bash shell variables

33 Answered Questions

[SOLVED] How do I parse command line arguments in Bash?

19 Answered Questions

[SOLVED] How do I tell if a regular file does not exist in Bash?

  • 2009-03-12 14:48:43
  • Bill the Lizard
  • 2441837 View
  • 3128 Score
  • 19 Answer
  • Tags:   bash file-io scripting

Sponsored Content