MH & nmh: Email for Users & Programmers

May, 2006

Looping Through a List of Arguments

You can use the Bourne shell's for loop to step through a list of arguments one by one. The C shell has a foreach loop with a different syntax. These loops aren't useful just for programming; they're good any time you need to repeat an operation on several messages or several folders.

The Bourne shell for loop looks like this:

    for arg in list
    do
        ...Handle $arg...
    done
    
If you omit the in list, the loop steps through the command-line arguments.

The loop puts its first argument in the arg shell variable, then executes the commands from do to done. Then it puts the next argument in arg, does the loop... and so on... ending the loop after handling all the arguments. For example, to search five folders for a message (and store a list of matches in the folder's picked sequence):

    for folder in inbox outbox top/sub1 top/sub2 project
    do
        echo Checking +$folder
        pick -subject "Something" -sequence picked +$folder
    done
    
That loop would print something like:
    Checking +inbox
    pick: No messages meet specification
    Checking +outbox
    2 hits
    Checking +top/sub1
        ...
    
Let's use an interactive example for the C shell. When you use a loop interactively, the shells print prompts:
    % foreach folder (inbox outbox top/sub1 top/sub2 project)
    ? echo Checking +$folder
    ? pick -subject "Something" -sequence picked +$folder
    ? end
    Checking +inbox
    pick: No messages meet specification
    Checking +outbox
    2 hits
    Checking +top/sub1
        ...
    
If you have a shell variable with one or more words in it, you can use that as the list for the loop. The list will be split at the spaces. For example, in the Bourne shell:
    msgs="first cur last"
        ...
    for msg in $msgs
    do
        ...Process $msg...
    done
    
The next Example shows how a Bourne shell script can find the options and arguments from a command line.

Example: for loop parsing a command line

    folder= switches=
    for arg
    do
       case "$arg" in
       [+@]*) # IT'S A FOLDER
          case "$folder" in
          "") folder="$arg" ;;
          *) echo "`basename $0`: '$arg'?  Only one folder at a time." 1>&2
             exit 1
             ;;
          esac
          ;;
       *) switches="$switches $arg" ;;
       esac
    done
    
Because the for arg line doesn't have a list of arguments, the loop gets and checks each argument from the command line. For an example, let's say that a user typed:
    % progname +project -nocc to
    
The first pass through the for loop, $arg is +project. Because the argument starts with a plus sign (+) or an at sign (@), the case treats it as a folder name. If there was a folder name in some earlier argument, an error is printed and the script exits (with a nonzero status, to signal an error). In this example, though, this is the first folder name, so +project is stored in in the folder shell variable. Then the loop repeats with the next argument.

The next argument, -nocc, doesn't look like a folder name. So the switches variable is replaced by its previous contents (an empty string), a space, and -nocc. The loop starts over once more, with to in $arg; this also doesn't look like a folder name, so now switches has its previous contents followed by a space and the new $arg. Now $switches is -nocc to. Because to was the last argument, the loop ends; $folder has the folder name and $switches has all the other arguments.