Shell How to tell if a program exists and is executable

alternate mode to walk path components without messing with ifs
Code:
#!/bin/sh
P=$PATH
while true
do
component=${P%%:*}
[ -z "$component" ] && break
echo $component
[ "$component" = "$P" ] && break
P=${P#*:}
done
 
covacat A deprecated "feature" of PATH is having an empty component mean "working directory". This shouldn't be supported any more, but I think the loop shouldn't just stop when hitting an empty component.

Actually, there's a related goof in my IFS-based solution as well, instead of ignoring an empty field, it will look for a binary directly in /.

Issue can be fixed with both approaches 😉 (exercise left to the reader)

In any case, you will need set -f to avoid pathname expansion in the test -x.

(edit: Just updated my example again because it also had two other flaws: set -f must be done before set --, otherwise the path components themselves are subject to pathname expansion, and the test must include testing for a "regular file", which includes symlinks, but will exlude directories)

(edit2: Just learned even the -a and -o operators of test(1) are non-portable, so avoid them as well)
 
Question if the result of a test is 0 , is this true. And nothing zero considered as false.
[ Let's say a program exists, with exit code 0. That's ok. Not zero is an error. ]
Or is it is vice verse ?
If i understand correctly:
A && B , is do B when A is true.
A || B , is do B when A is false.
 
Question if the result of a test is 0 , is this true. And nothing zero considered as false.
[ Let's say a program exists, with exit code 0. That's ok. Not zero is an error. ]
Yes, boolean logic in the shell is based on the exit status of executed commands and functions, and a non-zero exit status indicates an error condition.

Or is it is vice verse ?
It's the exact opposite in e.g. C (and most other languages that can convert a number to a boolean value).

If i understand correctly:
A && B , is do B when A is true.
A || B , is do B when A is false.
Yes, if A gets evaluated at all.

I just explained it more in depth here: https://forums.freebsd.org/threads/...am-exists-and-is-executable.92343/post-645001
 
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.
Result of switching to sh(1):
Code:
root@thinkpad:/usr/src # sh
root@thinkpad:/usr/src # which which > /dev/null 2> /dev/null
root@thinkpad:/usr/src #
Same machine as before.

Yeah, checking to see if a program exists and is executable or not is definitely not as simple as it should be... 😲
 
If you choose to consign both stdout and stderr to /dev/null, then you should be looking at the exit status...
 
How do I tell whether a program is installed and is executable from within a script?

I prefer (using sh / bash here, but similar for csh with setenv ... ):

Bash:
which -s ls && HAVE_LS=true || HAVE_LS=false

# Now you can use $HAVE_LS directly in other conditionals:
if $HAVE_LS; then
  ls foo/bar
else
  echo "Sorry, _ls_ is not available."
fi

With the -s switch, which(1) provides its only "output" via exit code, so no need to redirect anything, either.
 
Unfortunately, not portable. E.g. in Debain-based Linux distros which from debianutils doesn't provide such option.
Fair enough. For posix portability, use
Bash:
command -v foo >/dev/null && HAVE_FOO=true || HAVE_FOO=false
. Although this will also find functions and aliases.

Alternatively stick with which and drop the -s and redirect to /dev/null.
 
Back
Top