3148

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.
    – Tom Hale
    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?
    – jww
    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.
    – jarno
    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

39 Answers 39

4468

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 1
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 "$@"
    else
        date "$@"
    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."
6
  • 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
    Commented Jul 16, 2010 at 5:54
  • 48
    @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
    Commented Jul 19, 2010 at 13:43
  • 157
    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.
    – mikewaters
    Commented Dec 21, 2011 at 19:48
  • > Many operating systems have a which that doesn't even set an exit status, I'd be curious to see which OS's do this. "Many" feels like an exaggeration. Commented Aug 26, 2022 at 13:52
  • It should be noted that command -v returns true for functions. As such if you decided to wrap a program in a function with the same name, then you can't use command -v to check if the program is installed.
    – x-yuri
    Commented Feb 16 at 18:29
861

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]
Edit: This seems to be fixed as of dash 0.5.11 (Debian 11).

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

3
  • 4
    On some operating systems the empty string is treated as equivalent to . , so that [ -x '' ] may return true if the user has scan permission on the current directory; this leads to a false positive. (Fortunately this only affects a few older systems that are technically not POSIX compliant.) More resilient would be thing_path=$( command -v thing ) && [ -x "$thing_path" ] Commented Jan 2, 2023 at 3:24
  • 1
    Warning - this also fails for builtins: [ -x "$(command -v echo)" ]; echo $? prints 1!
    – spawn
    Commented Mar 28, 2023 at 11:18
  • @spawn right... with test also fails.
    – Smeterlink
    Commented Dec 24, 2023 at 16:09
234

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. Commented Feb 18, 2012 at 17:14
  • 1
    This wouldn't work on a standard sh: you &> isn't a valid redirect instructions.
    – jyavenard
    Commented Mar 4, 2012 at 11:19
  • 10
    @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
    Commented 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
    Commented 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
    Commented Dec 16, 2018 at 2:13
113

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.

1
  • 30
    You really shouldn't be using "which" for the reasons outlined in my comment.
    – lhunath
    Commented Mar 24, 2009 at 14:53
102

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"
fi
4
  • What does the &> do?
    – Saad Malik
    Commented Apr 18, 2016 at 19:51
  • 11
    The &> redirects both stdout and stderr together. Commented Apr 26, 2016 at 22:49
  • &> may not be available in your version of Bash. Marcello's code should work fine; it does the same thing. Commented Aug 2, 2016 at 1:28
  • 7
    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
    Commented Dec 16, 2018 at 2:14
49

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
5
  • 15
    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.
    – Palec
    Commented Dec 12, 2015 at 9:23
  • 2
    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.
    – dragon788
    Commented Jul 2, 2017 at 3:13
  • Why does this not work on if exists conda; then, even though anaconda is installed and returns: usage: conda [-h] [-V] command... when one enters conda in the terminal? (Note I verified your answer works with if exists bash; then on an Ubuntu 20 OS.)
    – a.t.
    Commented Oct 17, 2020 at 17:59
  • 1
    @a.t. which conda
    – Romário
    Commented May 27, 2021 at 18:47
  • @Palec it is not just aliases. command -v displays also built-ins, functions and even keywords and returns 0 for those!
    – jarno
    Commented Jan 18, 2022 at 16:15
33

Try using:

test -x filename

or

[ -x filename ]

From the Bash manpage under Conditional Expressions:

 -x file
          True if file exists and is executable.
2
  • 38
    That means you need to already know the full path to the application.
    – lhunath
    Commented Mar 24, 2009 at 12:45
  • 14
    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. Commented Mar 24, 2009 at 16:42
20

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.

2
  • 11
    Why not just if hash foo &> /dev/null; then ... ? Commented Nov 15, 2012 at 12:30
  • 2
    If set -e was set, the if... part was never run.
    – jarno
    Commented Jan 18, 2022 at 16:07
17

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.

5
  • 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.
    – algal
    Commented Feb 20, 2019 at 2:26
  • 2
    @algal You need to use quotes I think, so test -x "$(which <command>)"
    – JoniVR
    Commented Apr 2, 2019 at 12:19
  • @algal Perhaps ls is aliased? I dont think it would work if the command has parameter.
    – AnthonyWC
    Commented Apr 2, 2019 at 15:38
  • I can't vouch for this answer, but I'd also recommend quotes. $ test -x $(which absent_cmd) returns test: too many arguments, while test -x "$(which absent_cmd)" is properly parsed and results in exit code 1.
    – acorello
    Commented Dec 17, 2020 at 18:13
  • 2
    @acorello, the test: too many arguments suggests that which absent_cmd returns a multi-word error message. An executable file having the same name as the message could exist. 😁 Using which this way is just a bad idea.
    – Palec
    Commented Dec 23, 2021 at 14:57
15

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
else
    echo Not found
fi

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.

2
  • 1
    And some programs can be destructive as soon as you run them. Commented Apr 3 at 14:18
  • Also some existing programs don't understand --version, so Not found will be returned which would be wrong.
    – saulius2
    Commented Jul 17 at 14:37
15

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.

1
  • 2
    The [[ and the command substitution are useless here. Simply command -v mvn || exit will exit if mvn is not found; but then with that fixed this basically just repeats the accepted answer.
    – tripleee
    Commented Feb 25, 2022 at 12:59
13

It could be simpler, just:

#!/usr/bin/env bash                                                                
set -x                                                                             

# if local program 'foo' returns 1 (doesn't exist) then...                                                                               
if ! type -P foo; then                                                             
    echo 'crap, no foo'                                                            
else                                                                               
    echo 'sweet, we have foo!'                                                    
fi                                                                                 

Change foo to vi to get the other condition to fire.

NOTE: always handle the negative cases first, like if !. It's best to give the program every opportunity to fail; makes testing super easy ;-)

3
  • I intentionally made it as simple as possible. The only way for this to fail would likely be to mis-type the name of the program. Outside of that, it should work under any condition.
    – todd_dsm
    Commented Oct 11, 2021 at 21:24
  • Why did you enable printing commands as executed? (set -x)
    – jarno
    Commented Jan 18, 2022 at 16:05
  • set -x is the bash debugger. it helps to illustrate what's going on when people just copy/paste->execute a solution from sites like this; that's all. Comment/delete the line if you're done debugging.
    – todd_dsm
    Commented Jan 19, 2022 at 17:36
10

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?

1
  • 2
    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). Commented Feb 22, 2015 at 18:15
7

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
4
  • 4
    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.
    – user492203
    Commented Feb 2, 2017 at 14:42
  • @nyuszika7h But these tests may fail: For example builtin type -p if outputs nothing and returns 0 whether or not if exists.
    – jarno
    Commented Jan 18, 2022 at 15:11
  • @jarno What's your argument there? As you say, it both outputs nothing and returns 0, therefore it makes no difference if you use if builtin type -p if or if [ `builtin type -p if` ], except the latter is unnecessarily verbose and uses deprecated syntax.
    – user492203
    Commented Jan 18, 2022 at 15:18
  • @nyuszika7h They are both useless in testing the existence of a program called if.
    – jarno
    Commented Jan 18, 2022 at 15:22
7

I wanted the same question answered but to run within a Makefile.

install:
    @if [[ ! -x "$(shell command -v ghead)" ]]; then \
        echo 'ghead does not exist. Please install it.'; \
        exit -1; \
    fi
2
  • 2
    Relies on Bash being the shell due to the use of [[.
    – Palec
    Commented Dec 23, 2021 at 14:49
  • 1
    Using $(shell ...) within a recipe is almost always wrong. The accepted answer will work fine in a Makefile, too. exit -1 is not well-defined and the conditional is useless anyway.
    – tripleee
    Commented Feb 25, 2022 at 12:57
6

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.

2
  • Great list. From which versions? On my system command -v foo works on busybox sh (BusyBox v1.22.1 (Debian 1:1.22.0-19+b3) built-in shell (ash)). It fails correctly with 127 when foo is not found and prints the path if it finds it.
    – simohe
    Commented Oct 5, 2020 at 15:06
  • For example type -p if does not work.
    – jarno
    Commented Jan 18, 2022 at 18:27
6

zsh only, but very useful for zsh scripting (e.g. when writing completion scripts):

The zsh/parameter module gives access to, among other things, the internal commands hash table. From man zshmodules:

THE ZSH/PARAMETER MODULE
       The zsh/parameter module gives access to some of the internal hash  ta‐
       bles used by the shell by defining some special parameters.


[...]

       commands
              This  array gives access to the command hash table. The keys are
              the names of external commands, the values are the pathnames  of
              the  files  that would be executed when the command would be in‐
              voked. Setting a key in this array defines a new entry  in  this
              table  in the same way as with the hash builtin. Unsetting a key
              as in `unset "commands[foo]"' removes the entry  for  the  given
              key from the command hash table.

Although it is a loadable module, it seems to be loaded by default, as long as zsh is not used with --emulate.

example:

martin@martin ~ % echo $commands[zsh]
/usr/bin/zsh

To quickly check whether a certain command is available, just check if the key exists in the hash:

if (( ${+commands[zsh]} ))
then
  echo "zsh is available"
fi

Note though that the hash will contain any files in $PATH folders, regardless of whether they are executable or not. To be absolutely sure, you have to spend a stat call on that:

if (( ${+commands[zsh]} )) && [[ -x $commands[zsh] ]]
then
  echo "zsh is available"
fi
5

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

2
  • 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... Commented Feb 26, 2009 at 22:01
  • 15
    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.
    – lhunath
    Commented Mar 24, 2009 at 12:46
5

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.

2
  • 12
    However, the anti-pattern cmd; if [ $? -eq 0 ]; then should be refactored to if cmd; then
    – tripleee
    Commented Oct 21, 2013 at 7:04
  • This only works for libs installed via dpkg or apt Commented Nov 21, 2018 at 19:35
3

Use Bash builtins if you can:

which programname

...

type -P programname
4
  • 21
    Huh? which is not a Bash builtin.
    – tripleee
    Commented Oct 21, 2013 at 7:01
  • 1
    type -P programname is to be preferred, see accepted answer
    – RobertG
    Commented Jul 25, 2016 at 16:30
  • 1
    @RobertG All I see is that -P isn't POSIX. Why is type -P preferred? Commented Jan 2, 2018 at 18:24
  • 1
    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"
    – RobertG
    Commented Jan 5, 2018 at 10:06
3

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

    if [ -x /usr/bin/yum ]; then
        echo "This is Centos"
    fi
1
  • Yes i added this command if you need to install a package in the sevrer, Open suse, centos, Debian Commented Nov 29, 2018 at 14:11
2

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 }
4
  • Good point. However, when run from inside a bash script, aliases are not visible.
    – Basil Musa
    Commented Mar 23, 2016 at 16:08
  • 1
    There's also a problem, it will return false when the command 'alias' is checked. When it should return true. Example: test "alias"
    – Basil Musa
    Commented Mar 23, 2016 at 16:12
  • 2
    I just tested and using shopt -u expand_aliases ignores/hides these aliases and shopt -s expand_aliases shows them via command -v.
    – dragon788
    Commented Jul 2, 2017 at 3:07
  • Perhaps this could simply be construed as yet another reason to avoid aliases.
    – tripleee
    Commented Feb 25, 2022 at 13:01
1

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; }
1
  • 8
    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. Commented Apr 17, 2014 at 9:00
1

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
4
  • 1
    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.
    – Palec
    Commented Aug 26, 2015 at 10: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.
    – user619271
    Commented Aug 26, 2015 at 12:16
  • 1
    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
    Commented Aug 26, 2015 at 13:12
  • If it explained what is going on in these cases, it would be a helpful addendum to an answer.
    – Palec
    Commented Aug 26, 2015 at 16:06
1
#!/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

1
0

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

one_folder/process

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'
1
  • 5
    "For this the parent folder of one_folder must be in $PATH"—This is completely inaccurate. Try it. For this to work, one_folder must be in the current directory.
    – Wildcard
    Commented Jan 1, 2016 at 2:55
0

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
0

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
2
  • 3
    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.
    – tripleee
    Commented Sep 3, 2011 at 8:52
  • 4
    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. Commented Nov 15, 2012 at 12:33
0

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.

0

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
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.