## 35.21. Handling Command-Line Arguments with a for Loop

Sometimes you want a script that will step through the command-line arguments one by one. (The "$@" parameter (Section 35.20) gives you all of them at once.) The Bourne shell for loop can do this. The 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. It puts the first command-line argument in arg (or whatever else you choose to call the shell variable (Section 35.9)), then executes the commands from `do` to `done`. Then it puts the next command-line argument in arg, does the loop, and so on, ending the loop after handling all the arguments.

For an example of a for loop, let's hack on the zmore (Section 35.17) script.

`case` Section 35.11

>     #!/bin/sh
>     # zmore - Uncompress file(s), display with "more"
>     # Usage: zmore [more options] file [...files]
>     stat=1  # Default exit status; reset to 0 before normal exit
>     temp=/tmp/zmore$$
>     trap 'rm -f $temp; exit $stat' 0
>     trap 'echo "`basename $0`: Ouch! Quitting early..." 1>&2' 1 2 15

>     files=  switches=
>     for arg
>     do
>         case "$arg" in
>         -*) switches="$switches $arg" ;;
>         *)  files="$files $arg" ;;
>         esac
>     done

>     case "$files" in
>     "") echo "Usage: `basename $0` [more options] file [files]" 1>&2 ;;
>     *)  for file in $files
>         do
>             zcat "$file" | more $switches
>         done
>         stat=0
>         ;;
>     esac

We added a for loop to get and check each command-line argument. For example, let's say that a user typed the following:

>     % zmore -s afile ../bfile

The first pass through the for loop, `$arg` is `-s`. Because the argument starts with a minus sign (`-`), the case treats it as an option. Now the `switches` variable is replaced by its previous contents (an empty string), a space, and `-s`. Control goes to the esac and the loop repeats with the next argument.

The next argument, `afile`, doesn't look like an option. So now the `files` variable will contain a space and `afile`.

The loop starts over once more with `../bfile` in `$arg`. Again, this looks like a file, so now `$files` has `afile ../bfile`. Because `../bfile` was the last argument, the loop ends; `$switches` has the options and `$files` has all the other arguments.

Next, we added another for loop. This one has the word `in` followed by `$files`, so the loop steps through the contents of `$files`. The loop runs zcat on each file, piping it to more with any switches you gave.

Note that `$switches` isn't quoted (Section 27.12). This way, if `$switches` is empty, the shell won't pass an empty argument to more. Also, if `$switches` has more than one switch, the shell will break the switches into separate arguments at the spaces and pass them individually to more.

You can use a for loop with any space-separated (actually, IFS (Section 36.23)-separated) list of words -- not just filenames. You don't have to use a shell variable as the list; you can use command substitution (Section 28.14) (backquotes) or shell wildcards (Section 33.2), or just "hardcode" the list of words:

`lpr` Section 45.2

>     for person in Joe Leslie Edie Allan
>     do
>        echo "Dear $person," | cat - form_letter | lpr
>     done

The getopt and getopts (Section 35.24) commands handle command-line arguments in a more standard way than for loops.

-- JP

Copyright © 2003 O'Reilly & Associates. All rights reserved.