Cover V02, I05


New Messages

I noticed that the last five code lines of the weekdate program were not published in my article in the July/August magazine. Here are those lines, with the prior two for context.

# Set the exit status according to whether today is that day of the month
# If you'd rather output the day number, uncomment the next line and
# comment the test line.
echo $day

#test $day -eq `date '+%d'`

Larry Reznick
Rezolution Computer Consulting
9085 Burst Court
Sacramento, CA 95826!rezbook!reznick!rezbook!reznick

From: Greg A. Woods <!>

I had to laugh over the letter from Christopher Calabrese to Larry Reznick in the July/August 1993 issue of Sys Admin!

They're both "wrong"!

This is an excellent example of folks going out and making a whole lot of assumptions about how things should be, and how to go about something. Shell scripts aren't that hard to write, and it should be possible for anyone to learn most of the basic rules. No, you might never have time to master the subtleties of all the tools, but the basic (de facto) rules still apply, and should be followed.

First off, there is a valid reason for the construct "if test "X$1" = "X" ..." You must do this exactly this way if you wish to be portable to V7, 4.2BSD, etc. systems, for the following reasons:

- "test" is the only portable way to invoke the test(1) command or shell builtin.

- The leading "X" is to prevent parsing (by test(1)) of any values with leading dashes, such as '-r'.

Indeed "if [ -z "$1" ] ..." is prettier, but it's just not portable.

Second, you should never use the test(1) "=" operator to compare numeric values! Use "-eq", "-ne" and their friends.

Third, the usage message in Christopher's re-implementation did not follow any standard syntax I've ever run across! The "..." means the previous element may be repeated zero or more times.

Fourth, usage errors are often indicated by an exit code of two (2).

Fifth, the "#!" interpreter file magic "number" should be "#! ". The space is required by many 4.2BSD implementations, and is documented as so. This won't ensure that /bin/sh runs the script on some systems, such as older SCO Xenix machines where the user's shell is csh(1), and nor can any other portable solution, other than "rm /bin/csh"!

Oh, and in the first sed(1) expression, the dots don't have to be escaped with backslashes -- this is only necessary in the RE portion of a substitute expression.

As a final note, I consider the possible separate execution of two sed(1) processes to be a small price to pay for readability, and for the assurance of avoiding bugs due to filenames containing whitespace. Yes, such occurrences should be tracked down and eradicated, but it's the programmer's job to predict and avoid the consequences of all possible errors!

A better, different, but not much more portable, implementation, derived from Christopher's, is in Listing 1 (and yes, I tested this code!).

Of course all of this is moot for most sh(1) or ksh(1) users. They can use the "type" builtin [or for ksh(1), the "whence" builtin, which is an alias for "whence -v"] (except it doesn't set an exit code, nor bore you with the path it searched in, though it can identify shell functions and for ksh(1), aliases). The original csh(1) version of which(1) also knows to check for aliases. Note that for sh(1) users, an implementation of which(1) using "type" would have to be a shell function, set in $HOME/.profile or some such place, since any shell script would be unable to search for shell functions itself.

BTW, the font used for examples makes it impossible to distinguish between a single quote (') and a single back-quote (`).

Greg A. Woods
Larry Reznick responds: I agree with you completely that the "X$1" test has its place, given the conditions you outline. My inclusion of it in the original script was a holdover from when I knew less. Now that I know more, including what you point out, I won't apologize for it any more.

On other points: I also agree that test is the most portable way to handle the program name, but the brackets just seem more natural-looking to me. Now that you bring it up, I do notice where Christopher used the "=" operator -- definitely a string operator -- instead of the ".eq" operator. I missed that when I looked at his code. That sets up for strange troubles later.

About the usage error being 2: I've never seen that documented anywhere. The only documentation on the convention I've seen is the zero/non-zero convention. I'd be interested in knowing where you found such a standard. I usually adhere to local standards where I'm doing the work. Without such standards, I've used 1 for usage, 2 for file opening, 3 for read/write (3 for read & 4 for write when I need to distinguish), and 4 for memory. I don't recall if I've needed to go beyond that. This corresponds with the usual sequence of events in the program with the possible exchange of read/write & memory, according to the program.

BSD may require the "#!" be followed by a space. I don't have access to a BSD machine any more to verify that, so I trust you. However, SVR4 doesn't require the space. The documentation in csh(1) says, "the next word," when referring to what follows. That can certainly be interpreted as requiring a space, but I have a great deal of evidence that the space isn't required in SVR4: every shell script using that notation to start sh(1) but not having the space wouldn't run under csh. Here are the rules for naming the shell in a shell script:

1. If the first line begins with #!, what follows is the name of a shell or command to interpret the script.

2. When option 1 fails, if the first character of the first line begins with a #, csh is invoked.

3. When option 2 fails, invoke sh. As a tradition, I've seen a colon ':' used as the only character on the first line to avoid putting a # in the first position.

Based on these rules, the colon you write on the second line of your code serves no code purpose except that the shell interprets the colon as a null command, which does nothing successfully.

Finally, the which script that I use (I forgot about the type builtin -- thanks for the reminder) does announce aliases in precedence over commands using the same names. At the end of the original article, I challenged readers to add that facility. Thanks for your contributions.

MM responds: We have a filter to catch the back-quote problem but had not used it with New Messages! We will now do so.

From: Wolfgang Schrecker <!>
Subject: ftp-site

Hi, sorry to bother you. But as a customer who doesn't what to type all your goodies, is there a FTP site to get the sources & scripts?

Wolfgang Schrecker
Start Informatik GmbH

Look for the "Source Code Availability" reference in the contents on the cover of Sys Admin. All code listings are available either using UUCP or ftp. --rlw