In this section, we begin looking at Bourne shell control structures: programming features seldom used on the command line. The first construct we will consider is if, used for conditional command execution. Here is the simplest syntax of an if statement and a simple if example:
if condition then commands fi if test -x /sbin/sendmail ; then /sbin/sendmail $SENDMAIL_OPTIONS fi
The if command runs the commands in condition. If they return a true value (zero exit status), the commands are executed; on a false, nonzero status, the script jumps to the command after fi.
The preceding example uses the test command to check for the file /sbin/sendmail and starts the daemon if it's present and executable. We'll look at constructing conditions more closely a little later. For now, notice the placement of the then command. then must appear to the shell as a separate command, or you'll get an error. So it must be on a new line after the if command, or it must be separated from the if command by a semicolon. The same rules hold true for the fi command that ends the if construct.
There are more complex forms of if:
strings /vmunix | grep Unix > /tmp/motd i=`head -1 /etc/motd | grep -c Unix` if [ $i -eq 0 ] then cat /etc/motd >>/tmp/motd else tail +2 /etc/motd >>/tmp/motd fi mv /tmp/motd /etc/motd
This example illustrates the if-then-else construct. It updates the Unix version string in the message-of-the-day file. First, it gets the current Unix version string out of the kernel file /vmunix and puts it in the file /tmp/motd. Then, it checks whether the string "Unix" appears in the first line of /etc/motd. If it doesn't, the entire contents of /etc/motd are appended to /tmp/motd by the tail command. Otherwise when "Unix" does appear in the first line of /etc/motd all but its first two lines are appended to /tmp/motd. Finally, the new message file replaces the current one.
Here is an example of the most complex form of if:
set `who -r` Determine previous run level. if [ "$9" = "S" ] Previous level was single-user mode. then echo "The system is coming up." elif [ "$7" = "2" ]; then Target run level is level 2. echo "Changing to state 2." else echo "Changing to state 3." fi
The elif command allows if statements to be chained together. It functions as an else for the current if and as the beginning of a new if. The final else covers the case of all false conditions and ends the entire chain.
A.2.1 The test Command (a.k.a. [ )
The most common way to construct a condition for an if command is with the test command. It has two forms:
test condition [ condition ]
test evaluates condition and returns 0 or 1, depending on whether the condition is true (0) or false (1). (This polarity matches up with if's sense of true and false.)
The open bracket ([) command is a link to test and works in exactly the same way. It makes for more readable scripts, so you'll seldom see test. If the [ form is used, a final closed bracket (]) is included to keep test from complaining. Note that there must be spaces after [ and before ].
Table A-2 lists the various options and operators that may be used to construct conditions with test and [.
Many of the items in Table A-2 require quoting to protect them from the shell (as we'll see).
Here are some simple examples:
if [ "$9" = "S" ] If the 9th argument is S if [ -s /etc/ptmp ] If /etc/ptmp is not empty if [ $# -lt 4 ] If the number of arguments is < 4 if [ ! -f /etc/.fsckask ] If the plain file /etc/.fcskask does not exist if [ $? -eq 0 ] If the last command succeeded if [ $? -ne 0 ] If the last command failed
Here are some examples placed in context:
# get pid of lpsched pid=`/bin/ps -e | grep ' lpsched$' | sed -e 's/^ *//' -e 's/ .*//'` if [ $(pid) != "" ] If we found an lpsched process ... then /bin/kill $(pid) ... kill it. fi if [ $lx = autobootx ] If script argument was "autoboot", run fsck fi if [ -d /etc/rc0.d ] If there is a directory named /etc/rc0.d then run the K files fi if [ -x /sbin/inetd ]; then If the file /sbin/inetd is executable . . . /sbin/inetd . . . start the daemon echo inetd started fi if [ "$(BOOT)" = "yes" -a -d /etc/rc0.d ] then If this is a boot and there is an rc0.d directory Run the files in /etc/rc0.d fi
Note that constructs such as the following are used to prevent errors from occurring when a script's expected argument turns out to be null:
if [ $lx = autobootx ]
There are, of course, other ways of handling this contingency, but this approach is quite common in system scripts, especially older ones.
Here's a tricky one; try to figure out what this does:
interface_names="`echo /etc/dhcp.*[0-9] 2>/dev/null`" if [ "$interface_names" != '/etc/dhcp.*[0-9]' ]; then Configure the network interfaces with DHCP fi
A common mistake to make is to think the interface_name must always be the same as the filename string. The key here is to notice that the second operand to the not-equal operator in the if condition is a literal value: specifically, a string of characters and not a wildcarded filename. If there are any files of the form dhcp.xxxn in /etc (where xxx is a string and n is a number), the echo command returns the list of filenames. Otherwise, the literal string "/etc/dhcp.*[0-9]" is returned and becomes the value of interface_names.
The if command figures out which of these has happened. If interface_names has any value other than the literal wildcard string, the variable can be assumed to contain a list of filenames to be processed. On the other hand, if the variable holds only the wildcard string, then no files were found, and nothing needs to be done, so the commands in the body of the if block are skipped.