Differences between revisions 5 and 7 (spanning 2 versions)
Revision 5 as of 2007-05-13 15:37:29
Size: 1659
Editor: GreyCat
Comment: clean-up
Revision 7 as of 2007-10-11 14:23:06
Size: 3796
Editor: GreyCat
Comment: replace that horribly wrong example
Deletions are marked like this. Additions are marked like this.
Line 7: Line 7:
# This one works in bash
Line 14: Line 15:
If these builtins are not available (because you're in a Bourne shell, or whatever), then you may have to rely on the external command {{{which}}} (which is often a csh script, although sometimes a compiled binary). Unfortunately, {{{which}}} does ''not'' set a useful exit code (on any system but GNU/Linux) -- and it doesn't even write errors to stderr! Therefore, one must parse its output. Or, if you prefer {{{type}}}:

{{{
# Also a bash solution
if type -P qwerty >/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi
}}}

Korn shell has {{{whence}}} instead:

{{{
# Here's a ksh solution
if whence -p qwerty >/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi
}}}

If these builtins are not available (because you're in a Bourne shell, or whatever), then you may have to rely on the external command {{{which}}} (which is often a csh script, although sometimes a compiled binary). Unfortunately, {{{which}}} may not set a useful exit code, and it may not even write errors to stderr. In those cases, one must parse its output.
Line 18: Line 41:
# Backticks here because we're assuming a legacy Bourne shell.
x=`LC_ALL=C which qwerty 2>&1`
case "$x" in
  no\ *\ in\ *) echo qwerty does not exist;;
  *Command\ not\ found.) echo qwerty does not exist;;
  '') echo qwerty does not exist;;
  *) echo qwerty exists;;
esac
}}}
tmpval=`LC_ALL=C which qwerty 2>&1`
if test $rc -ne 0; then
  # FOR NOW, we'll assume that if this machine's which(1) sets a nonzero
  # exit status, that it actually failed. I've yet to see any case where
  # which(1) sets an erroneous failure -- just erroneous "successes".
  echo "qwerty is not installed. Please install it."
Line 28: Line 48:
Note that its output is ''not'' consistent across platforms. On HP-UX, for example, it prints {{{no qwerty in /path /path /path ...}}}; on OpenBSD, it prints {{{qwerty: Command not found.}}}; on Debian and SuSE, it prints nothing at all; and on Gentoo, it actually prints something to stderr.

{{{
# Another easy way that works only on gnu:
if ! which qwerty >/dev/null 2>&1; then
  echo "$0: install qwerty first"
  exit 1
else
    case "$tmpval" in
      *no\ *\ in\ *|*not\ found*|'')
        echo "qwerty is not installed. Please install it."
 ;;
      *)
        echo "Congratulations -- it seems you have qwerty (in $tmpval)."
 ;;
    esac
Line 38: Line 60:
(Although, on a GNU system, one would generally prefer to use one of the Bash builtins instead.) Note that `which(1)`'s output when a command is not found is ''not'' consistent across platforms. On HP-UX 10.20, for example, it prints {{{no qwerty in /path /path /path ...}}}; on OpenBSD 4.1, it prints {{{qwerty: Command not found.}}}; on Debian (3.1 and 4.0 at least) and SuSE, it prints nothing at all; on Red Hat 5.2, it prints {{{which: no qwerty in (/path:/path:...)}}}; on Red Hat 6.2, it writes the same message, but on standard error instead of standard output; and on Gentoo, it writes something on stderr.

So our best portable solution is to match the words `no` and `in`, or the phrase `not found`, in the combined stdout + stderr and pray.

Note to the person who tried to put a function in this FAQ as a legacy Bourne shell example: legacy Bourne shells ''don't have functions'', at all. POSIX shells have them, but the syntax is:

{{{
foo() {
  commands
}
}}}

You may ''not'' use the word `function`, and you may ''especially not'' use the combination of the word `function` and the `()` symbols. No shell but bash accepts that.

The approach used in `configure` scripts is usually to iterate over the components of `$PATH` and explicitly test for the presence of the command in each directory. That should tell you how unreliable `which(1)` is. Here's a ''simplified'' version of such a test:

{{{
save_IFS=$IFS
IFS=:
found=no
for dir in $PATH; do
  if test -x "$dir/qwerty"; then
    echo "qwerty is installed (in $dir)"
    found=yes
    break
  fi
done
IFS=$save_IFS
if test $found = no; then
  echo "qwerty is not installed"
fi
}}}

Real `configure` scripts are generally much more complicated than this, since they may deal with systems where `$PATH` is not delimited by colons; or systems where executable programs may have optional extensions like `.EXE`; or `$PATH` variables that have the current working directory included in them as an empty string; etc. If you're interested in such things, I suggest reading an actual GNU autoconf-generated `configure` script. They're far too large and complicated to include in this FAQ.

Anchor(faq81)

How can I determine whether a command exists anywhere in my PATH?

In BASH, there are a couple builtins that are suitable for this purpose: hash and type. Here's an example using hash:

# This one works in bash
if hash qwerty 2>/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi

Or, if you prefer type:

# Also a bash solution
if type -P qwerty >/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi

Korn shell has whence instead:

# Here's a ksh solution
if whence -p qwerty >/dev/null; then
  echo qwerty exists
else
  echo qwerty does not exist
fi

If these builtins are not available (because you're in a Bourne shell, or whatever), then you may have to rely on the external command which (which is often a csh script, although sometimes a compiled binary). Unfortunately, which may not set a useful exit code, and it may not even write errors to stderr. In those cases, one must parse its output.

# Last resort -- using which(1)
tmpval=`LC_ALL=C which qwerty 2>&1`
if test $rc -ne 0; then
  # FOR NOW, we'll assume that if this machine's which(1) sets a nonzero
  # exit status, that it actually failed.  I've yet to see any case where
  # which(1) sets an erroneous failure -- just erroneous "successes".
  echo "qwerty is not installed.  Please install it."

else
    case "$tmpval" in
      *no\ *\ in\ *|*not\ found*|'')
        echo "qwerty is not installed.  Please install it."
        ;;
      *)
        echo "Congratulations -- it seems you have qwerty (in $tmpval)."
        ;;
    esac
fi

Note that which(1)'s output when a command is not found is not consistent across platforms. On HP-UX 10.20, for example, it prints no qwerty in /path /path /path ...; on OpenBSD 4.1, it prints qwerty: Command not found.; on Debian (3.1 and 4.0 at least) and SuSE, it prints nothing at all; on Red Hat 5.2, it prints which: no qwerty in (/path:/path:...); on Red Hat 6.2, it writes the same message, but on standard error instead of standard output; and on Gentoo, it writes something on stderr.

So our best portable solution is to match the words no and in, or the phrase not found, in the combined stdout + stderr and pray.

Note to the person who tried to put a function in this FAQ as a legacy Bourne shell example: legacy Bourne shells don't have functions, at all. POSIX shells have them, but the syntax is:

foo() {
  commands
}

You may not use the word function, and you may especially not use the combination of the word function and the () symbols. No shell but bash accepts that.

The approach used in configure scripts is usually to iterate over the components of $PATH and explicitly test for the presence of the command in each directory. That should tell you how unreliable which(1) is. Here's a simplified version of such a test:

save_IFS=$IFS
IFS=:
found=no
for dir in $PATH; do
  if test -x "$dir/qwerty"; then
    echo "qwerty is installed (in $dir)"
    found=yes
    break
  fi
done
IFS=$save_IFS
if test $found = no; then
  echo "qwerty is not installed"
fi

Real configure scripts are generally much more complicated than this, since they may deal with systems where $PATH is not delimited by colons; or systems where executable programs may have optional extensions like .EXE; or $PATH variables that have the current working directory included in them as an empty string; etc. If you're interested in such things, I suggest reading an actual GNU autoconf-generated configure script. They're far too large and complicated to include in this FAQ.

BashFAQ/081 (last edited 2023-05-22 10:02:19 by emanuele6)