372 lines
16 KiB
HTML
372 lines
16 KiB
HTML
<!--startcut BEGIN header ==============================================-->
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
|
<HTML><HEAD>
|
|
<title>Chez Marcel LG #47</title>
|
|
</HEAD>
|
|
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#0000AF"
|
|
ALINK="#FF0000">
|
|
<!--endcut ============================================================-->
|
|
|
|
<H4>
|
|
"Linux Gazette...<I>making Linux just a little more fun!</I>"
|
|
</H4>
|
|
|
|
<P> <HR> <P>
|
|
<!--===================================================================-->
|
|
|
|
<center>
|
|
<H1><font color="maroon">Chez Marcel</font></H1>
|
|
<H4>By <a href="mailto:mcfly@workmail.com">Marty McGowan</a></H4>
|
|
</center>
|
|
<P> <HR> <P>
|
|
|
|
<!-- END header -->
|
|
|
|
|
|
|
|
|
|
<P> Marcel Gagné's article (<I>Linux Journal</I> #65, September 1999) on
|
|
French cooking inspired me to share some recipes of my own. The cooking
|
|
metaphor is not new to computing, as Donald Knuth, in his forward to
|
|
"Fundamental Algorithms" confesses he almost used "Recipes for the Computer" as
|
|
its title. Without stirring the metaphor too vigorously, Gagné's
|
|
article gives me the opportunity to share two items of interest and give them
|
|
the needed cooking flavor.
|
|
<P>
|
|
For some time, I've been concerned about what I regard are overuse or
|
|
misuse of two programming constructs:
|
|
<ul>
|
|
<li>temporary variables and files, and
|
|
<li>nested logic and decisions.
|
|
</ul>
|
|
To continue the cooking analogy, these two may be thought of
|
|
respectively as inconsistent or lumpy sauce, and uneven temperature.
|
|
Realizing that we chefs like to work on one another's recipes, lets
|
|
see what happens when we apply them to Marcel Gagné's recipe, "Check
|
|
User Mail".
|
|
<P>
|
|
Before I'd read Marcel's article, my style of programming used the tool
|
|
metaphor. While not much of a chef, I now prefer the cooking
|
|
metaphor, as it connotes more of a learning, and sharing model, which
|
|
is what we do in programming.
|
|
<P>
|
|
Marcel's recipe is an excellent starting point for my school of
|
|
cooking, as his recipe is complete, all by itself, and offers the
|
|
opportunity to visit each of the points once. First, here is a copy
|
|
of his recipe, without the comment header.
|
|
<pre>
|
|
for user_name in 'cat /usr/local/etc/mail_notify'
|
|
do
|
|
no_messages='frm $user_name |
|
|
grep -v "Mail System Internal Data" |
|
|
wc -l'
|
|
if [ "$no_messages" -gt "0" ]
|
|
then
|
|
echo "You have $no_messages e-mail message(s) waiting." > /tmp/$user_name.msg
|
|
echo " " >> /tmp/$user_name.msg
|
|
echo "Please start your e-mail client to collect mail." >> /tmp/$user_name.msg
|
|
/usr/bin/smbclient -M $user_name < /tmp/$user_name.msg
|
|
fi
|
|
done
|
|
</pre>
|
|
This script isn't hard to maintain or understand, but I think the
|
|
chefs in the audience will profit from the seasonings I offer here.
|
|
<P>
|
|
A by-product of my cooking school is lots of short functions. There
|
|
are those who are skeptical about adopting this approach. Let's
|
|
suspend belief for just a moment as we go through the method. I'll
|
|
introduce my seasonings one at a time, and then put Marcel Gagné's
|
|
recipe back together at the end. Then you may judge the sauce.
|
|
<P>
|
|
One of the languages in my schooling was Pascal, which if you recall
|
|
puts the main procedure last. So, I've learned to read scripts backwards,
|
|
as that's usually where the action is anyway. In Marcel Gagné's script,
|
|
we come to the point in the last line, where he sends the message
|
|
to each client. (I don't know Samba, but I assume this will make
|
|
a suitable function):
|
|
<PRE>
|
|
function to_samba { /usr/bin/smbclient -M $1; }
|
|
</PRE>
|
|
This presumes samba reads from its standard input without another flag
|
|
or argument. It's used: "to_samba {user_name}", reading the standard
|
|
input, writing to the samba client.
|
|
<P>
|
|
And, what are we going to send the user, but a message indicating they
|
|
have new mail. That function looks like this:
|
|
<PRE>
|
|
function you_have_mail {
|
|
echo "You have $1 e-mail message(s) waiting."
|
|
echo " "
|
|
echo "Please start your e-mail client to collect mail."
|
|
}
|
|
</PRE>
|
|
and it is used: you_have_mail {num_messages}. writing the
|
|
message on the standard output.
|
|
<P>
|
|
At this point, you've noticed a couple of things. The file names and
|
|
the redirection of output and input are missing. We'll use them if we
|
|
need them. But let me give you a little hint: we won't. Unix(Linux)
|
|
was designed with the principle that recipes are best made from simple
|
|
ingredients. Temporary files are OK, but Linux has other means to
|
|
reduce your reliance on them. Introducing temporary files does a few
|
|
things:
|
|
<ul>
|
|
<li>leaves you with extra cleaning to do after the meal is served,
|
|
<li>forces you to make sure someone else isn't using the bowl when
|
|
you need it for your recipe.
|
|
</ul>
|
|
Therefore, we seek to save ourselves these tasks. We'll see how this
|
|
happens in a bit.
|
|
<P>
|
|
A key piece of the recipe is deciding whether or not our user needs to
|
|
be alerted to incoming mail. Let's take care of that now:
|
|
<PRE>
|
|
function num_msg { frm $1 | but_not "Mail System Internal Data" | linecount; }
|
|
</PRE>
|
|
This is almost identical with Marcel's code fragment. We'll deal with
|
|
the differences later. The curious among you have already guessed.
|
|
This function is used: num_msg {user_name}, returning a count of the
|
|
number of lines.
|
|
<P>
|
|
What does the final recipe look like. All of Marcel Gagné's recipe is
|
|
wrapped up in this one line of shell program:
|
|
<PRE>
|
|
foreach user_notify 'cat /usr/etc/local/mail_notify'
|
|
</PRE>
|
|
And that's exactly how it's used. This single line is the entire
|
|
program, supported of course, by the functions, or recipe fragments we
|
|
have been building. We peeked ahead, breaking with Pascal tradition,
|
|
because, after looking at some low-level ingredients, I thought it
|
|
important to see where we are going at this point. You can see the
|
|
value of a single-line program. It now can be moved around in your
|
|
operations plan at will. You may serve your users with the frequency
|
|
and taste they demand. Note, at this point, you won't have much code
|
|
to change if you wanted to serve your A-M diners at 10 minute
|
|
intervals beginning at 5 after the hour and your N-Z diners on the
|
|
10-minute marks.
|
|
<P>
|
|
So what does "user_notify" look like? I toiled with this one. Let me
|
|
share the trials. First I did this:
|
|
<PRE>
|
|
function user_notify { do_notify $(num_msg $1) $1; }
|
|
</PRE>
|
|
thinking that if I first calculated the number of messages for the
|
|
user, and supplied that number and the user name to the function, then
|
|
that function (do_notify) could perform the decision and send the
|
|
message. Before going further, we have to digress. In the Korn
|
|
shell, which I use exclusively, the result of the operation in the
|
|
expression: $( ... ) is returned to the command line. So, in our
|
|
case, the result of "num_mag {user_name}" is a number 0 through some
|
|
positive number, indicating the number of mail messages the user has
|
|
waiting.
|
|
<P>
|
|
This version of user_notify would expect a "do_notify" to look like
|
|
this:
|
|
<PRE>
|
|
function do_notify { if [ "$1" -gt "0" ]; then notify_user $2 $1; fi; }
|
|
</PRE>
|
|
This is OK, but it means yet another "notify" function, and even for
|
|
this one-line fanatic, that's a bit much. So, what to do? Observe,
|
|
the only useful piece of information in this function is another
|
|
function name "notify_user". This is where culinary art, inspiration,
|
|
and experience come in. Let's try a function which looks like this:
|
|
<PRE>
|
|
function foo { { if [ "$X" -gt "0" ]; then eval $*; fi }
|
|
</PRE>
|
|
This is different than the "do_notify" we currently have. First of
|
|
all, $X, is not an actual shell variable, but here the X stands for
|
|
"lets see what is the best argument number to use for the numeric
|
|
test". And the "eval $*" performs an evaluation of all its
|
|
arguments. And here's the spice that gives this whole recipe it's
|
|
flavor! The first argument may be another command or function name!
|
|
A remarkable, and little used property of the shell is to pass command
|
|
names as arguments.
|
|
<P>
|
|
So, let's give "foo" a name. What does it do? If one of its
|
|
arguments is non-zero, then it performs a function (it's first
|
|
argument) on all the other arguments. Let's solve for X. It could be
|
|
any of the positional parameters, but to be completely general, it
|
|
probably should be the next one, as it's the only other one this
|
|
function ever has to know about. So, let's call this thing:
|
|
<PRE>
|
|
if_non_zero {function} {number} ....
|
|
</PRE>
|
|
Using another convenient shorthand, it all becomes:
|
|
<PRE>
|
|
function if_non_zero { [ $2 -gt 0 ] && eval $*; }
|
|
</PRE>
|
|
and we'll see how it's used later. With this function, user_notify now
|
|
looks like:
|
|
<PRE>
|
|
function user_notify { if_non_zero do_notify $(num_msg $1) $1; }
|
|
</PRE>
|
|
and is used: user_notify {user_name}. Note the dual use of the first
|
|
argument, which is the user's name. In one case, it is a further
|
|
argument to the num_msg function which return the number for that
|
|
user, in the other case, it merely stands for itself, but now as the
|
|
2nd argument to "do_notify". So, what does "do_notify" look like.
|
|
We've already written the sub pieces, so, it's simply:
|
|
<PRE>
|
|
function do_notify { you_have_mail $1 | to_samba $2; }
|
|
</PRE>
|
|
At this point, we have (almost) all the recipe ingredients. The
|
|
careful reader has noted the omission of "but_not", "linecount", and
|
|
"foreach". Permit me another gastronomic aside. Ruth Reichel,
|
|
recently food editor of the New York Times, is now the editor for
|
|
Gourmet magazine. One of the things she promises to do is correct the
|
|
complicated recipes so frequently seen in their pages. For example,
|
|
"use 1/4 cup lemon juice" will replace the paragraph of instructions
|
|
on how to extract that juice from a lemon.
|
|
<P>
|
|
In that spirit, I'll let you readers write your own "but_not" and
|
|
"linecount". Let me show you the "foreach" you can use:
|
|
<PRE>
|
|
function foreach { cmd=$1; shift; for arg in $*; do eval $cmd $arg; done; }
|
|
</PRE>
|
|
A slightly more elegant version avoids the temporary file name:
|
|
<PRE>
|
|
function foreach { for a $(shifted $*); do eval $1 $a; done; }
|
|
</PRE>
|
|
which requires "shifted":
|
|
<PRE>
|
|
function shifted { shift; echo $*; }
|
|
</PRE>
|
|
The former "foreach", to be completely secure, needs a "typeset"
|
|
qualifier in front of the "cmd" variable. It's another reason to
|
|
avoid the use of variable names. This comes under the general rule
|
|
that not every programming feature needs to be used.
|
|
<P>
|
|
We need one final "Chapters of the Cookbook" instruction before
|
|
putting this recipe back together. Let's imagine by now, that we are
|
|
practicing student chefs and we have a little repertoire of our own.
|
|
So what's an easy way to re-use those cooking tricks of the past. In
|
|
the programming sense, we put them in a function library and invoke
|
|
the library in our scripts. In this case, let's assume we have
|
|
"foreach", "but_not", and "linecount" in the cookbook. Put that file
|
|
"cookbook" either in the current directory, but more usefully,
|
|
somewhere along your PATH variable. Using Marcel Gagné's example, we
|
|
might expect to put it in, say, /usr/local/recipe/cookbook, so you
|
|
might do this in your environment:
|
|
<PRE>
|
|
PATH=$PATH:/usr/local/recipe
|
|
</PRE>
|
|
and then, in your shell files, or recipes, you would have a line like this:
|
|
<PRE>
|
|
. cookbook # "dot - cookbook"
|
|
</PRE>
|
|
where the "dot" reads, or "sources" the contents of the cookbook file into
|
|
the current shell. So, let's put it together:
|
|
<PRE>
|
|
# -- Mail Notification, Marty McGowan, mcfly@workmail.com, 9/9/99
|
|
#
|
|
. cookbook
|
|
# -------------------------------------------- General Purpose --
|
|
function if_non_zero { [ $2 -gt 0 ] && eval $*; }
|
|
function to_samba { /usr/bin/smbclient -M $1; }
|
|
# --------------------------------------- Application Specific --
|
|
function num_msg { frm $1 | but_not "Mail System Internal Data" | linecount; }
|
|
function you_have_mail {
|
|
echo "You have $1 e-mail message(s) waiting."
|
|
echo " "
|
|
echo "Please start your e-mail client to collect mail."
|
|
}
|
|
function do_notify { you_have_mail $1 | to_samba $2; }
|
|
function user_notify { if_non_zero do_notify $(num_msg $1) $1; }
|
|
#
|
|
# ------------------------------------------ Mail Notification --
|
|
#
|
|
foreach user_notify 'cat /usr/etc/local/mail_notify'
|
|
</PRE>
|
|
On closing, there are a few things that suggest themselves here.
|
|
"if_non_zero" probably belongs in the cookbook. It may already be in
|
|
mine. And also "to_samba". Where does that go? I keep one master
|
|
cookbook, for little recipes that may be used in any type of cooking.
|
|
Also, I keep specialty cookbooks for each style that needs its own
|
|
repertoire. So, I may have a Samba cookbook as well. After I've done
|
|
some cooking, and in a new recipe, I might find the need for some
|
|
fragment I've used before. Hopefully, it's in the cookbook. If it's
|
|
not there, I ask myself, "is this little bit ready for wider use?".
|
|
If so, I put it in the cookbook, or, after a while other little
|
|
fragments might find their way into the specialty books. So, in the
|
|
not too distant future, I might have a file, called "samba_recipe",
|
|
which starts out like:
|
|
<PRE>
|
|
# --------------- Samba Recipes, uses the Cookbook, Adds SAMBA --
|
|
. cookbook
|
|
# -------------------------------------------- General Purpose --
|
|
function to_samba { /usr/bin/smbclient -M $1; }
|
|
</PRE>
|
|
This leads to a recipe with three fewer lines and the cookbook
|
|
has been replace with 'samba_recipes" at the start.
|
|
<P>
|
|
Let me say just two things about style: my functions either fit
|
|
on one line or not. If they do, each phrase needs to be separated
|
|
by a semi-colon (;), if not, a newline is sufficient. My multi-line
|
|
function closes with a curly brace on it's own line. Also, my
|
|
comments are "right-justified", with two trailing dashes. Find your
|
|
style, and stick to it.
|
|
<P>
|
|
In conclusion, note how we've eliminated temporary files and variables.
|
|
Nor are there nested decisions or program flow. How was this
|
|
achieved? Each of these are now "atomic" actions. The one decision
|
|
in this recipe, "does Marcel have any mail now?" has been encapsulated
|
|
in the "if_non_zero" function, which is supplied the result of the
|
|
"num_msg" query. Also, the looping construct has been folded into the
|
|
"foreach" function. This one function has simplified my recipes
|
|
greatly. (I've also found it necessary to write a "foreach" function
|
|
which passes a single argument to each function executed.)
|
|
<P>
|
|
The temporary files disappeared into the pipe, which was Unix's (Linux's)
|
|
single greatest invention. The idea that one program might read its
|
|
input from the output from another was not widely understood when Unix
|
|
was invented. And the temporary names disappeared into the shell
|
|
variable arguments. The shell function, which is very well defined in
|
|
the Korn shell, adds greatly to this simplification.
|
|
<P>
|
|
To debug in this style, I've found it practical to add two things
|
|
to a function to tell me what's going on in the oven. For example:
|
|
<PRE>
|
|
function do_notify { comment do_notify $*
|
|
you_have_mail $1 | tee do_notify.$$ | to_samba $2
|
|
}
|
|
</PRE>
|
|
where "comment" looks like:
|
|
<PRE>
|
|
function comment { echo $* 1>&2; }
|
|
</PRE>
|
|
Hopefully, the chefs in the audience will find use for these
|
|
approaches to their recipes. I'll admit this style is not the easiest
|
|
to adapt, but soon it will yield recipes of more even consistency,
|
|
both in taste and temperature. And a programming style that will expand
|
|
each chef's culinary art.
|
|
|
|
|
|
|
|
<!-- BEGIN copyright ==================================================-->
|
|
<P> <hr> <P>
|
|
<H5 ALIGN=center>
|
|
|
|
Copyright © 1999, Marty McGowan<BR>
|
|
Published in Issue 47 of <i>Linux Gazette</i>, November 1999</H5>
|
|
<!-- END copyright ===================================================-->
|
|
|
|
|
|
|
|
|
|
|
|
<!--startcut footer ===================================================-->
|
|
<P> <hr> <P>
|
|
<A HREF="index.html"><IMG ALIGN=BOTTOM SRC="../gx/indexnew.gif"
|
|
ALT="[ TABLE OF CONTENTS ]"></A>
|
|
<A HREF="../index.html"><IMG ALIGN=BOTTOM SRC="../gx/homenew.gif"
|
|
ALT="[ FRONT PAGE ]"></A>
|
|
<A HREF="makarov.html"><IMG SRC="../gx/back2.gif"
|
|
ALT=" Back "></A>
|
|
<A HREF="../faq/index.html"
|
|
><IMG SRC="./../gx/dennis/faq.gif"
|
|
ALT="[ Linux Gazette FAQ ]"></A>
|
|
<A HREF="nielsen.html"><IMG SRC="../gx/fwd.gif" ALT=" Next "></A>
|
|
<P> <hr> <P>
|
|
</BODY></HTML>
|
|
<!--endcut ============================================================-->
|