type command bahaviour of FreeBSD's /bin/sh (Fwd: [issue426] ?hgmerge assumes FileMerge to be exist unconditionally on FreeBSD)

Oliver Fromme olli at lurza.secnetix.de
Fri Nov 24 05:21:57 PST 2006


NIIMI Satoshi wrote:
 > FreeBSD's /bin/sh produces:
 > | % sh -c 'type /nonexistent; echo $?'
 > | /nonexistent: No such file or directory
 > | 0

Note that the "type" utility is normally not called
with a full path, but with a command name.  In that
case it behaves correctly:

$ type asdfg
asdfg: not found
$ echo $?
127

If you have a full path name and need to know whether
it exists, you should rather use the "test" utility
instead of "type".  The purpose of "type" is to find
out how a _name_ would be interpreted when given as
a command (i.e. keyword, builtin, alias, shell function
or $PATH expansion).

 > Does the current behaviour of /bin/sh's type command conform to the standards?

SUSv3/POSIX2001 is not very clear, unfortunately.  It
doesn't exactly explain how the "type" utility should
behave when given a non-existing absolute path.  I tend
to believe that it's not a bug in FreeBSD, but rather
a valid interpretation of the standard.

 > > Because of the above feature (bug?), testing
 > > "/Developer/.../FileMerge" always succeeds and used unconditionally.
 > > I made a patch to work around this.

As I said above, it is _wrong_ to use "type" for testing
the existence of a file whose full path is known.  Use
"test" (a.k.a. "[") for that purpose:

if [ -f "$FULLPATH" ]; then
        # ... found
else
        # ... not found
fi

Even if you need to find out whether a name expands to
an external command, it is _not_ sufficient to only check
the return code of "type".  You also need to parse the
output in order to check whether it returned a full path
(as opposite to "shell builtin", "alias" or whatever).
In fact it is non-trivial, because the output format of
the "type" builtin is not standardized and varies between
shells.  Therefore, if you need to find a command in the
$PATH list, then it's better to do that yourself using
a shell loop, like this:

FULLPATH=""
OLD_IFS="$IFS"
IFS=":"
for DIR in $PATH; do
        if [ -x "$DIR/$COMMAND" ]; then
                FULLPATH="$DIR/$COMMAND"
                break
        fi
done
IFS="$OLD_IFS"
if [ -n "$FULLPATH" ]; then
        echo "Found: $FULLPATH"
else
	echo "$COMMAND not found"
fi

My conclusion is that you should _never_ use "type" in
portable shell scripts.

Best regards
   Oliver

-- 
Oliver Fromme,  secnetix GmbH & Co. KG, Marktplatz 29, 85567 Grafing
Dienstleistungen mit Schwerpunkt FreeBSD: http://www.secnetix.de/bsd
Any opinions expressed in this message may be personal to the author
and may not necessarily reflect the opinions of secnetix in any way.


More information about the freebsd-standards mailing list