As a way to learn I have been playing with different shells to see how portable I could do something and then came across this discussion on 'which' vs. 'command -v'.
https://unix.stackexchange.com/quest...at-to-use-then
Suffice to say I was not very happy with command -v, especially given it does not always have the same behavior in different shells (See mksh). So with some effort and several different iterations I came up with this function that serves most of my needs while working with at least ash (busybox and netbsd), bash, dash, ksh93, mksh, pdksh, yash and zsh.
Code:
exists () {
v=1
while [ "$#" -gt 0 ]; do
arg="$1"; shift
case "$arg" in ''|*/) continue ;; esac
x="${arg##*/}" z="${arg%/*}"
[ "$x" != "$z" ] || [ "$z/$x" = "$arg" ] && [ ! -f "$z/$x" ] && continue
[ "$x" = "$z" ] && [ -x "$z/$x" ] && [ ! -f "$arg" ] && z=
p="$z:$PATH"
while [ "$p" != "${p#*:}" ]; do
d="${p%%:*}" p="${p#*:}" b="$d/$x"
[ -f "$b" ] && [ -x "$b" ] && printf %s\\n "$b" && v=0 && break
done
done
return "$v"
}
Usage:
Code:
$ exists cat
/usr/bin/cat
$ exists cat ls awk
/usr/bin/cat
/usr/bin/ls
/usr/bin/awk
$ exists foo
$ echo $?
1
$ exists foo cat
/usr/bin/cat
$ echo $?
0
However I then wondered if this would work on even older true bourne shells (See hsh and osh) and it did not. The problem was the splitting of variables with "${foo##*bar}" which these limited shells did not have, note that they do not even have 'command'...
So I then changed it to use cut instead which does work. Though this predictably suffered from the problem of calling cut several times and a rather noticeable performance drop.
Code:
exists () {
v=1
while [ "$#" -gt 0 ]; do
arg="$1"; shift
case "$arg" in ''|*/) continue ;; esac
x="`printf %s "$arg" | cut -d '/' -f 2-`"
z="`printf %s "$arg" | cut -d '/' -f 1`"
[ "$x" != "$z" ] || [ "$z/$x" = "$arg" ] && [ ! -f "$z/$x" ] && continue
[ "$x" = "$z" ] && [ -x "$z/$x" ] && [ ! -f "$arg" ] && z=
p="$z:$PATH"
while [ "$p" != "`printf %s "$p" | cut -d ':' -f 1`" ]; do
d="`printf %s "$p" | cut -d ':' -f 1`"
p="`printf %s "$p" | cut -d ':' -f 2-`"
[ -f "$d/$x" ] && [ -x "$d/$x" ] && printf %s\\n "$d/$x" && v=0 && break
done
done
return "$v"
}
So my question is there a better way of doing this? Or is it as good as it gets?