A
for
loop (44.16
)
is great if you want to handle all of the command-line arguments to a
script, one by one. But, as is often the case, some arguments are
options that have their own arguments. For example, in the command
grep -f
filename
, filename
is an argument to
-f
; the option and its argument need to be processed
together. One good way to handle this is with a combination of
while
(44.10
)
,
test
(44.20
)
,
case
(44.5
)
,
and shift
.
Here's the basic construct:
while [ $# -gt 0 ]
do
case "$1" in
-a) options="$options $1";;
...
-f) options="$options $1"
argfile="$2"
shift
;;
*) files="$files $1";;
esac
shift
done
The trick is this: shift
removes an argument from the script's
argument list, shifting all the others over by one ($1
disappears,
$2
becomes $1
, $3
becomes $2
and so on).
To handle an option with its own argument, do another shift
.
The while
loop uses
test
(44.20
)
to check
that $#
- the number of arguments - is greater than zero, and
keeps going until this is no longer true, which only happens when
they have all been used up.
Meanwhile, all the case
has to do is to test $1
against the
desired option strings.
In the simple example shown above, we simply
assume that anything beginning with a minus sign is an option, which
we (presumably) want to pass on to some program that is being invoked
by the script.
So all we do is build up a shell variable that will
eventually contain all of the options.
It would be quite possible to
do anything else instead, perhaps setting other shell variables
or executing commands.
We assume that anything without a minus sign is a file.
This last
case could be written more robustly with a test
to be sure the
argument is a file.
Here's an example of a simple script that uses this construct to
pass an option and some files to pr
and from there to a program
that converts text to PostScript and on to the print spooler:
while [ $# -ne 0 ]
do
case $1 in
+*) pages="$1" ;;
*) if [ -f "$1" ]; then
files="$files $1"
else
echo "$0: file $1 not found" 1>&2
fi;;
esac
shift
done
pr $pages $files | psprint | lpr
This approach is perhaps obsolete if you have
getopts
(44.18
)
,
since getopts
lets you recognize option strings like
-abc
as being equivalent to
-a -b -c
but I still find it handy.
[In this example, it's essential.
The pr
option +
page-list
starts with a plus sign.
getopt
and getopts
don't support those old-style
options. -JP
]