Shell How to tell if a program exists and is executable

How do I tell whether a program is installed and is executable from within a script?

With some programs this works, eg

ls > null 2>&1 && echo exists

but this does not work on something like hexdump().

Does test() have an option for 'exists and is executable'? If it does I didn't notice it.
 
How do I tell whether a program is installed and is executable from within a script?
test(1)
Code:
     -x file       True if file exists and is executable.  True indicates only
                   that the execute flag is on.  If file is a directory, true
                   indicates that file can be searched.

Code:
#!/bin/sh

if [ -x /usr/local/bin/vim ]; then
  alias vi=vim
fi

# Or
[ -x /usr/local/bin/vim ] && alias vi=vim
 
I don't have a FreeBSD installation right now to test but on Linux bash this works like a charm:
Bash:
hash <program> 2>/dev/null || { echo "Script requires <program> but it's not installed. Aborting."; exit 1; }
 
test(1)
Code:
     -x file       True if file exists and is executable.  True indicates only
                   that the execute flag is on.  If file is a directory, true
                   indicates that file can be searched.
Oops... I'm sure I checked for that, maybe I need an eye test...

test -x hexdump || echo exists
 
Hi there,

I use command() :
Code:
~ > command -v pkg
/usr/sbin/pkg

Code:
~ > if command -v pkg > /dev/null ; then echo "installed" ; else echo "not installed" ; fi
installed

The answer from elgrande seems to be a good alternative.
But there is something wrong with the option -s on my part though.
It is suppose to not send any output but instead it's a bit verbose:

Code:
~ > which -s ls                    
/bin/ls -> /bin/ls
~ > which -s cp                    
cp: aliased to cp -v
~ > which -s apropos
/usr/bin/apropos -> /usr/bin/apropos

Am I the only one in this case ?

EDIT:
Forget about it, I currently use zsh and I guess the behaviour is bit different with it, when I did a try this time with sh it worked as intended.
Code:
~ $ which ls
/bin/ls
~ $ which -s ls
~ $ echo $SHELL
/bin/sh
 
I don't think this is going to give the results expected
Code:
$ test -x ksh ||echo exists
exists
$ test -x nosuchfile ||echo exists
exists
$ test -x $(which ksh) ||echo exists
$ test -x $(which nosuchfile) ||echo exists
$


This is probably what you want (does the file exist and is it executable)
Code:
$ [[ -f $(which ksh) && -x $(which ksh) ]] && echo yep || echo nope
yep
$ [[ -f $(which nosuchfile) && -x $(which nosuchfile) ]] && echo yep || echo nope
nope
$ touch bin/iamafile
$ chmod 444 bin/iamafile
$ ls -l bin/iamafile
-r--r--r-- 1 foghorn leghorn 0 Feb 15 08:03 bin/iamafile
$ [[ -f $(which iamafile) && -x $(which iamafile) ]] && echo yep || echo nope
nope
$ chmod 744 bin/iamafile
$ ls -l bin/iamafile
-rwxr--r-- 1 foghorn leghorn 0 Feb 15 08:03 bin/iamafile
$ [[ -f $(which iamafile) && -x $(which iamafile) ]] && echo yep || echo nope
yep
 
Code:
$ test -x ksh ||echo exists exists
Wrong, the echo is executed if the test fails.

Compare:
Code:
false || echo "is false"
true || echo "is true"
false && echo "is false"
true && echo "is true"

You really need to understand the difference between arg1 || arg2 and arg1 && arg2

- arg1 || arg2 arg2 is executed if arg1 returns false (rc != 0)
- arg1 && arg2 arg2 is executed if arg1 returns true (rc==0)
 
I know there have been a bunch of replies, but I'll share a method I use that hasn't been posted before. These use bash features that don't have anything to do with FreeBSD. Bash is a shell that runs in lots of places, and the execution is more or less the same in all those places.

In this example, I use a program called 'procs' if it is found, instead of the built in 'ps' command.
Code:
[[ "$(type -p procs)" != "" ]] && alias ps="$(type -p procs)"

The [[ is a bash comparison that does not launch a new program. Now, in bash [ is also a shell built-in, so it doesn't invoke a new program. But in some cases (often with /bin/sh instead of bash), writing if [ blah ] means invoking a whole separate program (/usr/bin/[).

Sometimes I need to do 2 things, so I use a full if/then:
Code:
if [[ "$(type -p exa)" != "" ]]
then
    alias ls="$(type -p exa)"
    export EXA_COLORS="blah blah blah"
fi

I'm using type -p because bash returns the path to the program. (e.g., type -p exa returns /usr/local/bin/exa) That way my alias command turns into alias ls=/usr/local/bin/exa. I could just do alias ls=exa and bash would find whatever version of exa is somewhere in my PATH. It would work, and that's normal. I'm being overzealous by hard-wiring the path (/usr/local/bin/exa) so that the PATH doesn't have to be searched. Probably a needless optimisation, but that's me in a nutshell. :)
 
Why make it complicated? 😲
Code:
$ whereis echo
/usr/bin/echo
$ ls -l /usr/bin/echo

Then use awk to parse the output, get perms as a separate sting, and look for 'x' character there... That kind of approach should work on any file/command, echo, hexdump, test, you name it.

I'd provide more details, but this post off the top of my head in 10 minutes is all I have time for at this time.
 
Ehm whereis -b echo would return a path to the executable, so there's really no need to check if it's executable or not.

But if you must;
Code:
#!/bin/sh

ECHO=$(whereis -b echo)

if [ -x "$ECHO" ]; then 
  $ECHO "Hello World!"
fi

# this will do the exact same thing
[ -x "$ECHO" ] && $ECHO "Hello World!"
 
Wrong, the echo is executed if the test fails.
Of course it does, Sherlock. That's what the OR means.

Are you unable to see the results from my output?

You really need to understand the difference between arg1 || arg2 and arg1 && arg2
Since I've been doing UNIX professionally for 30 years, meaning getting paid to do it, I fully understand the difference between OR and AND.
 
The [[ is a bash comparison that does not launch a new program. Now, in bash [ is also a shell built-in, so it doesn't invoke a new program. But in some cases (often with /bin/sh instead of bash), writing if [ blah ] means invoking a whole separate program (/usr/bin/[).

Incorrect; see builtin(1).

'test' aka '[' is a builtin in sh(1) (though not csh(1)) and so invokes no external program.
 
Ehm whereis -b echo would return a path to the executable, so there's really no need to check if it's executable or not.

But if you must;
Code:
#!/bin/sh

ECHO=$(whereis -b echo)

if [ -x "$ECHO" ]; then
  $ECHO "Hello World!"
fi

# this will do the exact same thing
[ -x "$ECHO" ] && $ECHO "Hello World!"
you want whereis -bq
 
which, whereis and friends are all not really portable, and you might even get different results with shells having them as builtins.

If you want to check whether a command exists (builtin or in the path), use command -v.

If you really want to check the path only, you'll have to "do it yourself", e.g. like this:

Bash:
#!/bin/sh
CMD="$1"
IFS=:
set -- ${PATH}
while [ -n "$1" ]; do
    if [ -x "$1/${CMD}" ]; then
        printf "%s\n" "$1/${CMD}"
        exit 0;
    fi
    shift
done
printf >&2 "%s: not found\n" "${CMD}"
exit 1;
 
How do I tell whether a program is installed and is executable from within a script? […]
You don’t. It’s a potential TOCTOU hence unnecessary overhead. Instead you presume any command may fail and take precautions against it. For non‑existent executables you could rely on the ‑e flag, see sh(1):​
Bash:
#!/bin/sh -e
foo
bar
aborts with the first non-existent command (no attempt at invoking bar made)​
Code:
foo: not found
unless you source the script.​
 
You don’t. It’s a potential TOCTOU hence unnecessary overhead.
Not necessarily. The hint is a good one though, you should never rely on something to "just work" later, just because you checked earlier. And if that's the only purpose of your check, it's indeed pointless.

Checks in advance still have their uses. You could e.g. in some larger script check for tools it absolutely needs in advance, then you reduce the risk to run into a "half done" job (and you can give the user a somewhat nice error message directly). You could even pick entirely different code paths if the tool in question is just an optional dependency. But whatever it is, you always have to be prepared for errors happening.
 
Just for completeness, here's a somewhat elaborate example what you can do portably (for POSIX systems) in shell script:

edit: Improved and commented the script, hopefully it's understandable for everyone now :)

Bash:
#!/bin/sh

# A "which"-like function strictly only looking for executables in a
# given path
_which()
{
    # check arguments and save the command to look for
    [ $# -lt 1 ] || [ $# -gt 2 ] && return 1
    _w_cmd="$1"
    _w_rc=1

    # replace positional parameters by path entries of the path passed,
    # falling back to ${PATH} from the environment.
    # Also disable pathname expansion (set -f), we want to check literal path
    # names in the following loop.
    _w_ifs=${IFS}
    _w_set=$(set +o)
    set -f
    IFS=:
    set -- ${2:-${PATH}}

    # check for executable in each path entry, stop indicating success on
    # first hit, ignore empty path entries
    for p; do
        if [ -n "$p" ] && [ -f "$p/${_w_cmd}" ] && [ -x "$p/${_w_cmd}" ]; then
            echo "$p/${_w_cmd}"
            _w_rc=0
            break;
        fi
    done

    # reset IFS and shell options and return success/failure
    eval ${_w_set}
    IFS=${_w_ifs}
    return ${_w_rc}
}

# Formatted output
alias pf='printf "%10s:  %s\n"'

# First walk the PATH from the environment
echo
echo full path:
echo
for c; do
    result=$(_which "${c}") \
        && pf $c "${result}" \
        || pf $c "** not found!"
done
echo

# 'getconf PATH' gives a minimal PATH to find all POSIX tools
echo POSIX path:
echo
for c; do
    result=$(_which "${c}" "$(getconf PATH)") \
        && pf $c "${result}" \
        || pf $c "** not found!"
done
echo

# Using "command -v", we also get functions, aliases and builtins (which then
# "shadow" any tools that might be available in the path).
# The output format is standardized in POSIX, so we can reliably interpret it.
# We *could* use a custom path here as well using env, e.g. for the POSIX path:
# $(env "PATH=$(getconf PATH)" command -v "${c}")
echo full path + builtins:
echo
for c; do
    result=$(command -v "${c}") \
        && case "${result}" in
            "alias "*) pf $c "<shell alias>";;
            /*) pf $c "${result}";;
            *) pf $c "<shell builtin or function>";;
        esac \
        || pf $c "** not found!"
done
echo

... in action:

Code:
$ ./testcmds.sh sh zsh test pf awk

full path:

        sh:  /bin/sh
       zsh:  /usr/local/bin/zsh
      test:  /bin/test
        pf:  ** not found!
       awk:  /usr/bin/awk

POSIX path:

        sh:  /bin/sh
       zsh:  ** not found!
      test:  /bin/test
        pf:  ** not found!
       awk:  /usr/bin/awk

full path + builtins:

        sh:  /bin/sh
       zsh:  /usr/local/bin/zsh
      test:  <shell builtin or function>
        pf:  <shell alias>
       awk:  /usr/bin/awk

$

I guess this also demonstrates why I recommend to not rely on tools like which. They aren't standardized, so even if present, might behave differently, e.g. it's unspecified how they would deal with shell builtins and so on.
 
I have always been using `which meh > /dev/null 2> /dev/null` and didn't notice any problems on the platforms I use.
 
I have always been using `which meh > /dev/null 2> /dev/null` and didn't notice any problems on the platforms I use.
Code:
root@thinkpad:/usr/src # which which > /dev/null 2> /dev/null
Ambiguous output redirect.
root@thinkpad:/usr/src # uname -a
FreeBSD thinkpad 13.2-RELEASE FreeBSD 13.2-RELEASE releng/13.2-n254617-525ecfdad 597 GENERIC amd64
 
Code:
root@thinkpad:/usr/src # which which > /dev/null 2> /dev/null
Ambiguous output redirect.
root@thinkpad:/usr/src # uname -a
FreeBSD thinkpad 13.2-RELEASE FreeBSD 13.2-RELEASE releng/13.2-n254617-525ecfdad 597 GENERIC amd64
Because in 13.2 the root's shell is csh, and its syntax is different:
Code:
% which which >& /dev/null
see csh(), in the section Input/output:
Code:
>& name
  ....
               The file name is used as standard output.
               ....
               The forms involving `&' route the diagnostic output into the
               specified file as well as the standard output.
 
csh is certainly not a good example, it doesn't follow bourne/POSIX syntax....

I have always been using `which meh > /dev/null 2> /dev/null` and didn't notice any problems on the platforms I use.

But then see e.g. zsh (and then switching to base sh):

Code:
$ command -v which
which
$ command -v test
test
$ which test
test: shell built-in command
$ sh
$ command -v which
/usr/bin/which
$ command -v test
test
$ which test
/bin/test
$

There's nothing wrong with which(1) for interactive use, you will immediately notice if something is "fishy". There's also nothing wrong with using which when you know the environment and shell your script will run on.

But if you need to stay portable, you have two issues with which. It isn't exactly specified what it does, and it isn't even guaranteed to exist. Therefore, for portable scripts:
  • Use command -v if you want to cover all sorts of "command-like" things in a shell. Thanks to the standardized output format, you can tell what you got (an actual path, a function or builtin, or an alias).
  • Use a shell function like in my example above if you actually want to search the path for binaries. To be fully robust, you should additionally set -f to disable pathname expansion, otherwise it can give wrong results when that path or binary name contain special shell pattern characters... (edit: I just updated my example above to also temporarily set -f to be really sure it always works)
 
- arg1 || arg2 arg2 is executed if arg1 returns false (rc != 0)
- arg1 && arg2 arg2 is executed if arg1 returns true (rc==0)
A little nitpick on this (it's correct if nothing else preceeds it as written), just because I've seen related confusion on these forums about it:

In a POSIX-compliant shell:
  • something after || is evaluated if $? is currently non-zero
  • something after && is evaluated if $? is currently zero
The important things here:
  • If the condition isn't met, what follows isn't even evaluated but just skipped
  • It doesn't matter "where" $? was last set
  • There's no "expression tree" or similar for constructs using || and &&, they are just "batch processed" from left to right, deciding whether or not to evaluate the following code each time || or && is encountered
This explains behavior like this:
Code:
$ false && echo "true" || echo "false"
false
$ false || echo "false" && echo "true"
false
true
Now, from this example, you could think there was an expression tree with && taking precedence over ||, but no, there isn't, see:
Code:
$ false && echo "true" || echo "false" || echo "true" && echo "false"
false
false

With some tree using precedence, the second "false" wouldn't be printed. What actually happens is strictly linear
  • false -> $? becomes 1
  • && echo "true" -> skip this, $? remains 1
  • || echo "false" -> evaluate and execute, $? becomes 0
  • || echo "true" -> skip this, $? remains 0
  • && echo "false" -> evaluate and execute
 
Back
Top