229

I am writing my first shell script. In my script I would like to check if a certain command exists, and if not, install the executable. How would I check if this command exists?

if # Check that foobar command doesnt exist
then
    # Now install foobar
fi
3

8 Answers 8

344

In general, that depends on your shell, but if you use bash, zsh, ksh or sh (as provided by dash), the following should work:

if ! type "$foobar_command_name" > /dev/null; then
  # install foobar here
fi

For a real installation script, you'd probably want to be sure that type doesn't return successfully in the case when there is an alias foobar. In bash you could do something like this:

if ! foobar_loc="$(type -p "$foobar_command_name")" || [[ -z $foobar_loc ]]; then
  # install foobar here
fi
Sign up to request clarification or add additional context in comments.

10 Comments

I like this answer. I tried not to be too swayed by ivants' image which I like even more ;)
hmm...when I change it to say if ! type "foo" > /dev/null; then I get the output on the screen "myscript.sh: line 12: type: foo: not found", however, it still seems to work because when I say if ! type "ls" > /dev/null; there is no output and the if statement does not get executed (since it returns true). How can I silence the output when the command doesnt exist?
Andrew, try if ! type "foo" > /dev/null 2>&1;
> /dev/null 2>&1 is the same as &> /dev/null
The man page for type is very poor on my distro (Arch Linux). The help text is better, albeit only slightly. type: usage: type [-afptP] name [name ...] . The man page shows "options" as "none".
|
62

Five ways, 4 for bash and 1 addition for zsh:

  • type foobar &> /dev/null
  • hash foobar &> /dev/null
  • command -v foobar &> /dev/null
  • which foobar &> /dev/null
  • (( $+commands[foobar] )) (zsh only)

You can put any of them to your if clause. According to my tests (https://www.topbug.net/blog/2016/10/11/speed-test-check-the-existence-of-a-command-in-bash-and-zsh/), the 1st and 3rd method are recommended in bash and the 5th method is recommended in zsh in terms of speed.

7 Comments

Check if a program exists from a Bash script specifically advises against using which.
Also echo =foobar in zsh. Note that your zsh solution only returns true/false, but doesn't print the pathname if existant. Also echo $commands[foo] won't return non-zero if foo doesn't exist. For a function that works in both shells, see here
@Raptor if examines exit code.
the third option seems to be the only one with same behaviour in zsh and bash. For the others, zsh writes to stdout on error while bash writes to stderr.
@tubbo (( $+commands[foobar] )) works for me. Could you describe a scenario in which it fails?
|
42

Try using type:

type foobar

For example:

$ type ls
ls is aliased to `ls --color=auto'

$ type foobar
-bash: type: foobar: not found

This is preferable to which for a few reasons:

  1. The default which implementations only support the -a option that shows all options, so you have to find an alternative version to support aliases

  2. type will tell you exactly what you are looking at (be it a Bash function or an alias or a proper binary).

  3. type doesn't require a subprocess

  4. type cannot be masked by a binary (for example, on a Linux box, if you create a program called which which appears in path before the real which, things hit the fan. type, on the other hand, is a shell built-in (yes, a subordinate inadvertently did this once).

2 Comments

Could you put this in the form of an if/else statement (one that doesn't output to the console)?
And on some custom Linux, there isn't ''which'' command.
33

The question doesn't specify a shell, so for those using fish (friendly interactive shell):

if command -v foo > /dev/null
  echo exists
else
  echo does not exist
end

For basic POSIX compatibility, we use the -v flag which is an alias for --search or -s.

5 Comments

doesn't work for aliases
@Herrgott that's because command -v/-s searches in the $PATH. Aliases in fish are functions, and functions are searched for in the $fish_function_path. Many ways to check if an alias exists, e.g. if alias | grep -w 'myalias' >/dev/null; echo ok; end.
Doesn't this execute the command too?
@AaronFranke it only searches for the executable, doesn't execute it.
You can use --query and avoid the redirect: fishshell.com/docs/current/cmds/command.html. While not POSIX-compliant, command is built into Fish.
22

Check if a program exists from a Bash script covers this very well. In any shell script, you're best off running command -v $command_name for testing if $command_name can be run. In bash you can use hash $command_name, which also hashes the result of any path lookup, or type -P $binary_name if you only want to see binaries (not functions etc.)

Comments

3

A function which works in both bash and zsh:

# Return the first pathname in $PATH for name in $1
function cmd_path () {
  if [[ $ZSH_VERSION ]]; then
    whence -cp "$1" 2> /dev/null
  else  # bash
     type -P "$1"  # No output if not in $PATH
  fi
}

Non-zero is returned if the command is not found in $PATH.

Comments

1

which <cmd>

also see options which supports for aliases if applicable to your case.

Example

$ which foobar
which: no foobar in (/usr/local/bin:/usr/bin:/cygdrive/c/Program Files (x86)/PC Connectivity Solution:/cygdrive/c/Windows/system32/System32/WindowsPowerShell/v1.0:/cygdrive/d/Program Files (x86)/Graphviz 2.28/bin:/cygdrive/d/Program Files (x86)/GNU/GnuPG
$ if [ $? -eq 0 ]; then echo "foobar is found in PATH"; else echo "foobar is NOT found in PATH, of course it does not mean it is not installed."; fi
foobar is NOT found in PATH, of course it does not mean it is not installed.
$

PS: Note that not everything that's installed may be in PATH. Usually to check whether something is "installed" or not one would use installation related commands relevant to the OS. E.g. rpm -qa | grep -i "foobar" for RHEL.

5 Comments

which has other pitfalls as well
I was just starting to read man type to see how it's better.. may be you can save me some time by posting it here.. :)
The real trouble with which is that it is an external command, and won't be able to deal with the specifics of the current shell session internals, as it can have no knowledge of them.
Check if a program exists from a Bash script specifically advises against using which.
it's best to not use which here, other answers are safer
1

A function I have in an install script made for exactly this

function assertInstalled() {
    for var in "$@"; do
        if ! which $var &> /dev/null; then
            echo "Install $var!"
            exit 1
        fi
    done
}

example call:

assertInstalled zsh vim wget python pip git cmake fc-cache

4 Comments

heads up: this crashes zsh as is
would only crash if you are calling this outside a script or sourcing the script. Change exit 1 to return 1 or some other error code if you are doing so.
Check if a program exists from a Bash script specifically advises against using which.
I used this but changed which to command -v

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.