21

I'm trying to write all of my sh startup/env scripts to work with as much DRY and as much: "works on every *nix I clone it to", as possible. This means making sure that if I try to run code that's not there, that the code fails gracefully. To that end I need to be able to test if programs exist. I know how to test if a file exists, but I'm not sure how to test to see if an application is executable within the path. I'd rather use the $PATH, as some of these need to work on arch, ubuntu, and centos. Some might be installed in my homedir, on systems where I don't have root, others might not be installed, and others yet my be installed in system paths.

2
22

Use type commandname. This returns true if commandname is anything executable: alias, function, built-in or external command (looked up in $PATH). Alternatively, use command commandname which returns true if commandname is a built-in or external command (looked up in $PATH).

exists () {
  type "$1" >/dev/null 2>/dev/null
}

There are a few sh variants (definitely pre-POSIX; I know of /bin/sh under OSF1 ≤3.x and some versions of the Almquist shell found in early NetBSD versions and a few 20th-century Linux distributions) where type always returns 0 or doesn't exist. I don't think any systems shipped with these this millennium. If you ever encounter them, here's a function you can use to search in $PATH manually:

exists () { (
    IFS=:
    for d in $PATH; do
      if test -x "$d/$1"; then return 0; fi
    done
    return 1
) }

This function is generally useful if you want to exclude built-ins and functions and look up the name in $PATH. Most shells have a built-in for this, command -v, though it's a relatively recent addition to POSIX (still optional as of POSIX:2004). It's basically a programmer-friendly version of type: it prints the full path for an executable in $PATH, the bare name for a built-in or function, and an alias definition for an alias.

exists_in_path () {
  case $(command -v -- "$1") in
    /*) return 0;;
    alias\ *) return 1;; # alias
    *) return 1;; # built-in or function
  esac
}

Ksh, bash and zsh also have type -p to look up only executables in $PATH. Note that in bash, the return status of type -p foo is 0 if foo is a built-in or function; if you want to test for an executable in $PATH, you need to check that the output is not empty. type -p is not in POSIX; for instance Debian's ash (which is /bin/sh on Ubuntu) doesn't have it.

6
  • @gilles, will this (or something similar) if [ type keychain ]; then not work? I get error /home/xenoterracide/.zshrc:84: parse error: condition expected: type I suppose I could write the function exists... I just thought this might be simpler in a sense... Dec 19 '10 at 0:00
  • @xenoterracide: Drop the brackets! Dec 19 '10 at 0:03
  • I think you are looking for if type $APP >/dev/null 2>/dev/null; then ... You don't want the [].
    – Steven D
    Dec 19 '10 at 0:05
  • 4
    Bah, I knew I should have refreshed. Beaten by Gilles again!
    – Steven D
    Dec 19 '10 at 0:06
  • type -p if you're specifically looking for a command in $PATH (not aliases or functions or builtins).
    – ephemient
    Jan 8 '11 at 6:34
2

If you are only looking for external programs, you can also use which. Don't know how portable that is though.

1
  • 4
    In theory, it's less portable than type or command; which is not in POSIX, for instance. In practice, which does exist almost everywhere, but in some places (where it's implemented as a csh script) it uses a different path (due to a .cshrc), which defeats the purpose. Dec 19 '10 at 13:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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