Classic Shell Scripting - Arnold Robbins [105]
test -z "$result" && warning "$pattern: not found"
done
At the completion of the outer loop, we have looked for every requested match in every directory in the search path, and we are ready to return to the invoking program. There is only one small problem left to deal with: user exit-code values are limited to the range 0 through 125, as shown in Table 6-5 in Chapter 6, so we cap the EXITCODE value at 125:
test $EXITCODE -gt 125 && EXITCODE=125
Our program is almost complete: its last statement returns to the parent process with an explicit exit status, as all well-behaved Unix programs should. That way, the parent can test the exit status to determine whether the child process succeeded or failed:
exit $EXITCODE
In Example 8-1, we present the complete text of pathfind, without our commentary, so that you can see it as the shell sees it. The total length is about 90 lines, ignoring comments and empty lines.
Example 8-1. Searching a path for input files
#! /bin/sh -
#
# Search for one or more ordinary files or file patterns on a search
# path defined by a specified environment variable.
#
# The output on standard output is normally either the full path
# to the first instance of each file found on the search path,
# or "filename: not found" on standard error.
#
# The exit code is 0 if all files are found, and otherwise a
# nonzero value equal to the number of files not found (subject
# to the shell exit code limit of 125).
#
# Usage:
# pathfind [--all] [--?] [--help] [--version] envvar pattern(s)
#
# With the --all option, every directory in the path is
# searched, instead of stopping with the first one found.
IFS='
'
OLDPATH="$PATH"
PATH=/bin:/usr/bin
export PATH
error( )
{
echo "$@" 1>&2
usage_and_exit 1
}
usage( )
{
echo "Usage: $PROGRAM [--all] [--?] [--help] [--version] envvar pattern(s)"
}
usage_and_exit( )
{
usage
exit $1
}
version( )
{
echo "$PROGRAM version $VERSION"
}
warning( )
{
echo "$@" 1>&2
EXITCODE=`expr $EXITCODE + 1`
}
all=no
envvar=
EXITCODE=0
PROGRAM=`basename $0`
VERSION=1.0
while test $# -gt 0
do
case $1 in
--all | --al | --a | -all | -al | -a )
all=yes
;;
--help | --hel | --he | --h | '--?' | -help | -hel | -he | -h | '-?' )
usage_and_exit 0
;;
--version | --versio | --versi | --vers | --ver | --ve | --v | \
-version | -versio | -versi | -vers | -ver | -ve | -v )
version
exit 0
;;
-*)
error "Unrecognized option: $1"
;;
*)
break
;;
esac
shift
done
envvar="$1"
test $# -gt 0 && shift
test "x$envvar" = "xPATH" && envvar=OLDPATH
dirpath=`eval echo '${'"$envvar"'}' 2>/dev/null | tr : ' ' `
# sanity checks for error conditions
if test -z "$envvar"
then
error Environment variable missing or empty
elif test "x$dirpath" = "x$envvar"
then
error "Broken sh on this platform: cannot expand $envvar"
elif test -z "$dirpath"
then
error Empty directory search path
elif test $# -eq 0
then
exit 0
fi
for pattern in "$@"
do
result=
for dir in $dirpath
do
for file in $dir/$pattern
do
if test -f "$file"
then
result="$file"
echo $result
test "$all" = "no" && break 2
fi
done
done
test -z "$result" && warning "$pattern: not found"
done
# Limit exit status to common Unix practice
test $EXITCODE -gt 125 && EXITCODE=125
exit $EXITCODE
Let's wrap up this section with some simple tests of our program, using a search path, PATH, that Unix systems always have. Each test includes a display of the exit code, $?, so that we can verify the error handling. First, we check the help and version options:
$ pathfind -h
Usage: pathfind [--all] [--?] [--help] [--version] envvar pattern(s)
$ echo $?
0
$ pathfind --version
pathfind version 1.0
$ echo $?
Next, we provoke some error reports with bad options, and missing arguments:
$ pathfind --help-me-out
Unrecognized option: --help-me-out
Usage: pathfind [--all] [--?] [--help] [--version] envvar pattern(s)
$ echo $?
1
$ pathfind
Environment variable missing or empty
Usage: pathfind [--all] [--?] [--help] [--version]