April 12, 2004

CLI magic: test this

Author: Joe Barr

Last week I mentioned in closing that there would be a test. I hope you took that seriously, you malingering, mousiefied, GUI-addicts, because this week is test week. So brace yourself for a blast of fresh air and open a console window, we're going into command-line territory.

The test command allows you to conditionally perform some task based on the existence, type, or status of a file. It also lets you do the same thing by comparing integer and string values. For my sake, we'll keep it as simple as possible and stick to file tests while still showing off some of its potential.

You've got mail, or do you?

OK. You're not running AOL, but you want to know when you get mail. We can do that with the following command. Granted, a beep is not quite as nice as a human voice announcement, but it's fine for our purposes.

The command below tests for the existence of /var/mail/username (that's what the -e argument does) AND sounds a beep if it is found. Like this:


test -e /var/spool/mail/username && beep

Note the "&&" operator in the command above, separating the test command from the beep command. That's a logical AND operator, which requires both sides of the command to be true or else it won't execute. If the /var/spool/mail/username file does not exist, the test won't be true and the beep won't sound.

What if you want to hear the beep only when you don't have mail? Cool. Not a problem. Just add a NOT operator in front of the -e argument. The ! mark is the NOT operator, so our revised test command would look like this:


test ! -e /var/spool/mail/username && beep

Of course, if you just want to hear a beep every few minutes, you can change the logical AND to a logical OR operator ("||"), then it will beep regardless of whether the file exists or not.

Correction: A reader pointed out that the OR operator used above would not result in a beep if both sides were true. In other words, if the file were there, it would not beep. Thanks, Jim.

In addition to testing for the existence of a file, you can test to see if a file is a directory by using the -d argument, or to see if it exists and contains data by using the -s argument, or many other optional tests.

In our case, the username file remains in existence even after your mail client has removed the mail. That means our test with the "-e" option is meaningless, since it will always be true whether you actually have mail or not. What we really want to use is the "-s" option, which ensures the file size is greater than zero.

Typing that entire command each time we want to check mail is silly, so let's make a script out of it and save it as a file called mailcheck.


#!/bin/sh
#
test -s /var/spool/mail/username && echo "You have mail"

Note that in the script above beep was changed to an echo of "You have mail." That's because beep -- which on my system is an alias of echo `echo -en "\007" -- would not work from within the script. Maybe a reader can explain that mystery in a comment.

Another test

Normally, I would tell you at this point to do "man test" for more information. But this week, I'm going to suggest you try "info test" instead. Why? Because that's where you will learn that
not only is the test command provided as a part of the GNU core utilities, most shells -- including Bash -- have a built-in test command with very similar functionality.

According to info:

Because most shells have a built-in command by the same name, using
the unadorned command name in a script or interactively may get you
different functionality than that described here.

The Bash test built-in in provided primarily to provide flow of control in shell scripts without having to call external programs. That suggests that our mailcheck script could be written as an IF statement.

Let's give it a try. The Bash test has two forms. The first is the command followed by an expression and the second is the expression enclosed in brackets. We'll add both forms to our script so that it looks like this:


#!/bin/sh
#

test -s /var/spool/mail/username && echo "You have mail"

if test -s /var/spool/mail/username
then
echo "form 1 says You have mail."
fi

if [ -s /var/spool/mail/username ]
then
echo "form 2 says You have mail."
fi

On my system, all three forms of test in the script work just fine. If they don't work on yours, verify that you've changed username to your actual account name and try it again.

Click Here!