Online Book Reader

Home Category

Classic Shell Scripting - Arnold Robbins [104]

By Root 852 0
eval statement. We have the name of the environment variable in envvar, available as "$envvar", but we want its expansion. We also want to turn the colon separators into normal whitespace separators. We therefore construct the argument string '${'"$envvar"'}', which the shell expands to the equivalent of '${MYPATH}', if MYPATH were the name supplied by the user. The surrounding single quotes protect it from further expansion. That string is then given to eval, which sees two arguments: echo and ${MYPATH}. eval looks up MYPATH in the environment, finding, say, /bin:/usr/bin:/home/jones/bin, and then evaluates the expanded command echo /bin:/usr/bin:/home/jones/bin, which in turn sends /bin:/usr/bin:/home/jones/bin down the pipe to the tr command, which converts colons to spaces, producing /bin /usr/bin /home/jones/bin. The surrounding backquotes (or $(...) in modern shells) turn that into the value assigned to dirpath. We silence any errors from eval by the usual technique of sending them to /dev/null:

dirpath=`eval echo '${'"$envvar"'}' 2>/dev/null | tr : ' ' `

It took a long paragraph to explain the single short statement that sets dirpath, so you can see that it is tricky. Clearly, eval adds significant power to the language.

After eval, understanding the rest of the program is pretty easy. First there are some sanity checks to handle any unusual conditions that would cause problems later on: every good program should make such checks, to avoid the infamous garbage-in, garbage-out syndrome. Notice that the last sanity check, for an empty file list, does not cause an error report. The reason is that any program that processes a list should always handle an empty list gracefully: if there is nothing to do, there is nothing to report but success:

# 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

We then have three nested loops: the outer one over the argument files or patterns, the middle one over the directories in the search path, and the inner one over matching files in a single directory. We want the loops in that order so that each file is dealt with completely before moving on to the next one. The reverse loop order would just prove confusing to the user, since the file reports would be mixed up. Before starting the middle loop, we set result to the empty string, since we use that later to determine whether anything was found:

for pattern in "$@"

do

result=

for dir in $dirpath

do

for file in $dir/$pattern

do

In the body of the innermost loop, test -f tells us whether $file exists and is a regular file. (It is also true if it is a symbolic link that ultimately points to a regular file.) If it does, we record it in result, report it on standard output with an echo command, and if the default of reporting only the first one applies, we break out of the innermost and middle loops. Otherwise, the loop continues over the remaining matching files, possibly producing more reports:

if test -f "$file"

then

result="$file"

echo $result

test "$all" = "no" && break 2

fi

done

done

In this program, there is no need in the middle loop to test whether $dir itself exists as a valid directory because that is subsumed by the existence check in the innermost loop for $file. However, with a more complex loop body, such a test would be desirable, and can be easily done with a single statement: test -d $dir || continue.

At the completion of the middle loop, we have searched all of the directories in the search path for $pattern, and result either holds the name of the last match found or is still empty, if no matches were found.

We test whether the expansion $result is empty, and if so, we report the missing file on standard error, increment the error count in EXITCODE (inside the warning function), and then continue the outer loop with the next file:

Return Main Page Previous Page Next Page

®Online Book Reader