Home Check if a program exists from a Bash script
Reply: 28

Check if a program exists from a Bash script

gregh Published in 2009-02-26 21:52:49Z

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.

Cory Klein
Cory Klein Reply to 2017-09-26 23:02:09Z


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


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 "$@"
        date "$@"
Adam Davis
Adam Davis Reply to 2009-02-26 21:56:53Z

The which command might be useful. man which

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


       which - locate a command


       which [-a] filename ...


       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.


       -a     print all matching pathnames of each argument


       0      if all specified commands are found and executable

       1      if one or more specified commands is  nonexistent  or  not  exe-

       2      if an invalid option is specified

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


Hengjie Reply to 2012-11-26 11:19:09Z

Try using:

test -x filename


[ -x filename ]

From the bash manpage under Conditional Expressions:

 -x file
          True if file exists and is executable.
dreamlax Reply to 2009-02-26 22:01:52Z

It depends 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
    echo does not exist

otherwise use

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

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

Steven Penny
Steven Penny Reply to 2013-01-24 06:28:09Z

Why not use Bash builtins if you can?

which programname


type -P programname
Steven Penny
Steven Penny Reply to 2013-01-24 06:27:47Z

I never did get the above solutions 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
Nathan Crause
Nathan Crause Reply to 2010-01-15 23:16:17Z

For those interested, none of the methodologies above 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 distro):

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

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

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.

Bono Reply to 2015-11-22 10:36:52Z

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)
Josh Strater
Josh Strater Reply to 2010-10-14 09:24:40Z

I have a function defined in my .bashrc that makes this easier.

command_exists () {
    type "$1" &> /dev/null ;

Here's an example of how it's used (from my .bash_profile.)

if command_exists mvim ; then
    export VISUAL="mvim --nofork"
Veger Reply to 2014-07-23 20:31:46Z

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: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html

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

Greg K
Greg K Reply to 2011-04-08 15:30:28Z

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

Hope this help someone else!

tim Reply to 2011-06-01 17:49:22Z

To mimic Bash's type -P cmd we can use 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!'; }

type ls
env -i type ls

env -i type $cmd 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }
Community Reply to 2017-05-23 11:55:11Z

If there is no 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="`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;;
   ' _ "$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 10.6.8 using Bash 4.2.24(2) command -v ls does not match a moved /bin/ls-temp.

anycast.cw Reply to 2011-12-14 12:41:26Z

The hash-variant has one pitfall: On the command line you can for example type in


to have process executed. For this the parent folder of one_folder must be in $PATH. But when you try to hash this command, it will always succeed:

hash one_folder/process; echo $? # will always output '0'
Anonymous Reply to 2012-02-18 07:54:44Z
checkexists() {
    while [ -n "$1" ]; do
        [ -n "$(which "$1")" ] || echo "$1": command not found
0xF Reply to 2013-07-08 15:14:30Z

If you check for program existence, you are probably going to run it later anyway. Why not try to run it in the first place?

if foo --version >/dev/null 2>&1; then
    echo Found
    echo Not found

It's a more trustworthy check that the program runs than merely looking at PATH directories and file permissions.

Plus you can get some useful result from your program, such as its version.

Of course the drawbacks are that some programs can be heavy to start and some don't have a --version option to immediately (and successfully) exit.

user2961933 Reply to 2013-11-06 19:06:48Z

I second the use of "command -v". E.g. like this:

md=$(command -v mkdirhier) ; alias md=${md:=mkdir}  # bash

emacs="$(command -v emacs) -nw" || emacs=nano
alias e=$emacs
[[ -z $(command -v jed) ]] && alias jed=$emacs
Ciro Santilli 华涌低端人口 六四事件 法轮功
Ciro Santilli 华涌低端人口 六四事件 法轮功 Reply to 2016-12-08 13:25:29Z

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 printf "OK\n"; else printf "missing\n"; fi

Sample output:

latex     OK
pandoc    missing

Adjust the 10 to the maximum command length. Not automatic because I don't see a non verbose way to do it.

blueyed Reply to 2014-07-21 00:18:23Z

hash foo 2>/dev/null: works with zsh, bash, dash and ash.

type -p foo: it appears to work with zsh, bash and ash (busybox), but not dash (it interprets -p as an argument).

command -v foo: works with zsh, bash, dash, but not ash (busybox) (-ash: command: not found).

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

nyuszika7h Reply to 2017-10-26 14:18:46Z

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

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


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

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.

ThCTLo Reply to 2015-03-27 14:26:27Z

my setup for a debian server. i had a the problem when multiple packages contains 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"
        echo "Package $1 is NOT available for install"
        echo  "We can not continue without this package..."
        echo  "Exitting now.."
        exit 0
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"
            echo  "installing package : $1, please wait.."
            _apt_install $1
            sleep 0.5

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"
            echo  "installing package : $1, please wait.."
            _apt_install_norecommends $1
            sleep 0.5
user619271 Reply to 2015-08-26 09:23:12Z

If you guys can't get the things above/below to work and 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"

Second. It can give you no output at all.

$ command -v nvm
$ bash -c "command -v nvm"
$ bash -c "nvm --help"
bash: nvm: command not found
A.N Reply to 2015-09-22 00:48:43Z

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


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

It uses shell builtin and program echo status to stdout and nothing to stderr by the other hand if a command is not found, it echos status only to stderr.

Romário Reply to 2015-12-11 20:42:45Z

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:

  command -v "$1" >/dev/null 2>&1

Here's how to use it:

if exists bash; then
  echo 'Bash exists!'
  echo 'Your system does not have Bash'
nodakai Reply to 2016-03-10 02:31:54Z

I'd say there's no 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 is no superior to alias here.) There's no portable solution for it, and even a Bash-specific solution is rather tedious.

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

test() { command -v $1 | grep -qv alias }
rpr Reply to 2016-06-18 23:19:53Z

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" = "" ]
  echo "The time program does not exist on this system."
  exit 1

# 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
Da'Jour Reply to 2017-04-16 01:21:00Z
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
    exit 1                           # EXIT THE PROGRAM IF IT DOES NOT


exit 0                               # EXIT THE PROGRAM IF IT DOES
ecwpz91 Reply to 2017-09-06 00:42:27Z



# 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
bash -c 'printf "Fin.\n"'


✘ [ABRT]: notacmd: command does not exist
hits    command
   0    /usr/bin/bash
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.448014 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO