3214

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.

7
  • 3
    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. Commented Dec 16, 2018 at 2:12
  • 1
    Also relevant is How to 'hash -r' and refresh all shells? and When to rehash executables in $PATH with bash? Commented Jan 30, 2019 at 5:10
  • @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. Commented Jan 18, 2022 at 15:28
  • What is your input? Commented Mar 21, 2023 at 13:30
  • @jano Since the context is bash, type would be preferable over which, since we know that it is available. Howeever, both type and which do not detect, whether a program exists, but whether is is found in the PATH. To see whether a program of a certain name "exists" in a file system, you would have to use find. Commented Mar 21, 2023 at 13:33

38 Answers 38

1
2
0
#!/bin/bash
a=${apt-cache show program}
if [[ $a == 0 ]]
then
echo "the program doesn't exist"
else
echo "the program exists"
fi

#program is not literal, you can change it to the program's name you want to check

Sign up to request clarification or add additional context in comments.

1 Comment

The curly braces are a syntax error. apt-cache is not a standard utility, and only exists on Debian-based distributions. Why is testing ”$?” to see if a command succeeded or not, an anti-pattern?
-2

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.

Comments

-2

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.

3 Comments

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.
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.
@Palec I am not sure how this is difficult. It's actually simpler as it relies on the shell to let the user know that the command can not be found and exits with an error, as requested by the OP. Let me know what I missed :)
-2

Late answer but this is what I ended up doing.

I just check if the command I execute returns an error code. If it returns 0 it means program is installed. Moreover you can use this to check the output of a script. Take for instance this script.

foo.sh

#!/bin/bash
echo "hello world"
exit 1 # throw some error code

Examples:

# outputs something bad... and exits
bash foo.sh $? -eq 0 || echo "something bad happened. not installed" ; exit 1

# does NOT outputs nothing nor exits because dotnet is installed on my machine
dotnet --version $? -eq 0 || echo "something bad happened. not installed" ; exit 1

Basically all this is doing is checking the exit code of a command run. the most accepted answer on this question will return true even if the command exit code is not 0.

3 Comments

I'm not 100% certain if your last statement is correct. The most accepted answer uses the command command with the flag -v to quickly validate if its argument is a built-in or executable command that is found in PATH. So the statement if ! command -v STRING; then ...; fi will validate if STRING is a valid command.
This is a different approach where it checks the exit code of the last command executed. The accepted answer is probably best for most of the cases. This is an alternative if you want to go deeper.
This seems confused at the very least. It cannot work for e.g. false which returns a nonzero error code by design.
-3
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

1 Comment

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

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.

1 Comment

type is (almost) fine, but the pretzel logic to check its result is probably what caused the downvotes here. See stackoverflow.com/questions/36371221/…
-4

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

2 Comments

This only works for Debian based systems like Ubuntu.
Even for those systems it can fail if the command is not installed via dpkg or apt.
-5

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

1 Comment

What if I want to do something else instead of exiting the script? Not all commands would have a --version or --help option.
1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.