This section describes other important Bourne shell and bash control structures. A.3.1 The while and until CommandsThe while statement is one way to create a loop. It has two forms: while condition do commands done until condition do commands done In the while form, the commands are executed until the condition becomes false. In the until form, they are executed until the condition becomes true. Here is an example of while: cat /etc/fstab | while read DEVICE MOUNT_DIR READONLY FS DUMMY1 DUMMY2 do fsck (if required) and mount the device done This loop takes each line of /etc/fstab in turn (sent to it via cat) and performs an appropriate action for the corresponding device. The while loop will end when read (described later) returns a nonzero status, indicating an end-of-file. Here is another very similar example, taken from a recent Linux system: while read des fs type rest; do case "$fs" in /) break;; *) ;; esac done < /etc/fstab if [ -e "$des" -a "$type" != "resiserfs" ] then run fsck fi Note that the input to the while loop is provided via I/O redirection following the done statement. A.3.2 The case CommandThe case command is a way to perform a branching operation. Here is its syntax: case str in pattern_1) commands ;; pattern_2) commands ;; ... pattern_n) commands ;; *) commands ;; esac The value in str is compared against each of the patterns. The corresponding commands are executed for the first match that is found. The double semicolons are used to end each section. Wildcards are allowed in the patterns, and a pattern consisting of a single asterisk can serve as a default if no other pattern is matched; it must be placed at the end of the case command. Here is an example of the case command: /etc/fsck -p >/dev/console case $? in Select action based on fsck return value. 0) date >/dev/console ;; 2) exit 1 ;; 4) /sbin/reboot -n ;; *) echo "Unknown error in reboot" > /dev/console exit 1 ;; esac In this example, different commands are run depending on the return value from fsck. Another typical use of case is found in the files in /etc/init.d on systems with System V-style boot scripts. Here is an abbreviated example: #! /bin/sh # Start or stop the lp scheduler case "$1" in 'start') /usr/lib/lpsched # and other commands ;; 'stop') /usr/lib/lpshut > /dev/null 2>&1 ;; 'restart') $0 stop && $0 start ;; *) echo "usage: $0 {start|stop}" ;; esac This script takes different actions depending on the keyword specified as its argument. The argument it gets at boot time depends on whether it is invoked as an S-file or a K-file (as we noted in Chapter 4). A.3.3 The for CommandThe for command is another way to create loops. Here is its syntax: for var [ in list ] do commands done If a list is included, the variable var is set to each value in turn, and the command in the loop are executed. If no list of values is specified, $@ (all script arguments) is used. Here is an example: for d in /tmp /usr/tmp /chem/tmp ; do find $d ! -name tmp -type d -exec rmdir {} \; done This loop removes empty subdirectories from under /tmp, /usr/tmp, and /chem/tmp in turn, while not removing those directories themselves (via ! -name tmp of course, it won't remove /tmp/tmp either). A.3.3.1 The bash arithmetic for loopbash also offers an arithmetic-style for loop, with the following syntax: for (( start ; test ; incr )) ; do commands done start is an expression evaluated when the loop starts, test is an expression evaluated at the end of each loop iteration, and incr is an expression evaluated whenever the test condition is false. The loop terminates when the test condition is true. Here is a simple example: for (( i=1 ; i<10 ; i++ )); do echo $i done This loop displays the numbers 0 through 9. A.3.4 The Null CommandOccasionally, you'll run across a command consisting of just a colon: : This null command is typically used when all the work is done in the control statement, and the body of the loop is empty. Sometimes this command is used as a comment character (since its arguments will be ignored), as in this ancient example: : attempt to ship remaining files uucico -r However, this practice is not recommended, because a line such as the following: : Hourly cleanup script @(#)cleanup.hourly 2/4/90 (part of which was produced by a source-code control system) produces an error: ./cleanup.hourly: syntax error at line 2: `(' unexpected This is because syntax checking is still done on the arguments to the null command. |