14.10. The Command Line
14.10.1 Processing Command-Line Options with
getopts
If you are writing scripts that require a number of command-line options, positional parameters are not always the most efficient. For example, the UNIX
ls
command takes a number of command-line options and arguments. (An option requires a leading dash; an argument does not.) Options can be passed to the program in several ways:
ls “laFi
,
ls “i “a “l “F
,
ls “ia “F
, and so forth. If you have a script that requires arguments, positional parameters might be used to process the arguments individually, such as
ls “l “i “F
. Each dash option would be stored in
$1
,
$2
, and
$3
, respectively. But, what if the
user
listed all of the options as one dash option, as in
ls “liF
? Now the
“liF
would all be assigned to
$1
in the script. The
getopts
function makes it possible to process command-line options and arguments in the same way they are
processed
by the
ls
program.
[8]
The
getopts
function will allow the
runit
program to process its arguments using any variety of combinations.
[8]
See the UNIX/Linux manual pages (Section 3) for the C library function
getopt
.
Example 14.70.
(The Command Line)
1 $
runit x n 200 filex
2 $
runit xn200 filex
3 $
runit xy
4 $
runit yx n 30
5 $
runit n250 xy filey
(any other combination of these arguments)
EXPLANATION
-
The program
runit
takes four arguments:
x
is an option,
n
is an option requiring a number argument after it, and
filex
is an argument that stands alone.
-
The program
runit
combines the options
x
and
n
and the number argument
200
;
filex
is also an argument.
-
The program
runit
is invoked with the
x
and
y
options combined.
-
The program
runit
is invoked with the
y
and
x
options combined; the
n
option is passed separately, as is the number argument,
30
.
-
The program
runit
is invoked with the
n
option combined with the number argument, the
x
and
y
options are combined, and the
filey
is separate.
Before getting into all the details of the
runit
program, we examine the line from the program where
getopts
is used to see how it processes the arguments.
Example 14.71.
(A line from the script called runit)
while getopts :xyn: name
EXPLANATION
x
,
y
, and
n
are the options. In this example the first option is preceded by a
colon
. This
tells
getopts
to use silent error reporting. If there is a colon after one of the options, the option expects an argument separated from it by whitespace. An argument is a word that does not begin with a dash.
“n
requires an argument.
Any options typed at the command line must begin with a dash.
Any options that do not contain a dash tell
getopts
that the option list has ended.
Each time
getopts
is called, it places the
next
option value it finds in the variable
name
. (You can use any variable name here.) If an illegal argument is given,
name
is assigned a question mark.
Sample
getopts
Scripts
The following examples
illustrate
how
getopts
processes arguments.
Example 14.72.
(The Script)
#!/bin/bash
# Program: opts1
# Using getopts First try
1
while getopts xy options
do
2 case $options in
3 x) echo "you entered x as an option";;
y) echo "you entered y as an option";;
esac
done
(The Command Line)
4 $
opts1 x
you entered x as an option
5 $
opts1 xy
you entered x as an option
you entered y as an option
6 $
opts1 y
you entered y as an option
7 $
opts1 b
opts1: illegal option -- b
8 $
opts1 b
EXPLANATION
-
The
getopts
command is used as a condition for the
while
command. The valid options for this program are listed after the
getopts
command; they are
x
and
y
. Each option is
tested
in the body of the loop, one after the other. Each option will be assigned to the variable
options
, without the leading dash. When there are no longer any arguments to process,
getopts
will exit with a nonzero status,
causing
the
while
loop to terminate.
-
The
case
command is used to test each of the possible options found in the
options
variable, either
x
or
y
.
-
If
x
was an option, the string
you entered -x as an option
is displayed.
-
At the command line, the
opts1
script is given an
x
option, a legal option to be processed by
getopts
.
-
At the command line, the
opts1
script is given an
xy
option;
x
and
y
are legal options to be processed by
getopts
.
-
At the command line, the
opts1
script is given a
y
option, a legal option to be processed by
getopts
.
-
The
opts1
script is given a
b
option, an illegal option.
Getopts
sends an error message to
stderr
.
-
An option without a dash prepended to it is not an option and causes
getopts
to stop processing arguments.
Example 14.73.
(The Script)
#!/bin/bash
# Program: opts2
# Using getopts Second try
1
while getopts xy options 2> /dev/null
do
2 case $options in
x) echo "you entered x as an option";;
y) echo "you entered y as an option";;
3
\?) echo "Only -x and -y are valid options" 1>&2;;
esac
done
(The Command Line)
$
opts2 x
you entered x as an option
$
opts2 y
you entered y as an option
$
opts2 xy
$
opts2 xy
you entered x as an option
you entered y as an option
4 $
opts2 g
Only -x and -y are valid options
5 $
opts2 c
Only -x and -y are valid options
EXPLANATION
-
If there is an error message from
getopts
, it is redirected to
/dev/null
.
-
If the option is a bad option, a question mark will be assigned to the
options
variable. The
case
command can be used to test for the question mark, allowing you to print your own error message to standard error.
-
If the
options
variable is assigned the question mark, the
case
statement is executed. The question mark is protected with the backslash so that the shell does not see it as a wildcard and try to perform filename substitution.
-
g
is not a legal option. A question mark is assigned to the variable
options
and the error message is displayed.
-
c
is not a legal option. A question mark is assigned to the variable
options
and the error message is displayed.
Special
getopts
Variables
The
getopts
function provides two variables to help keep track of arguments:
OPTIND
and
OPTARG
.
OPTIND
is a special variable that is
initialized
to one and is incremented each time
getopts
completes processing a command-line argument to the number of the next argument
getopts
will process. The
OPTARG
variable contains the value of a legal argument. See Examples 14.74 and 14.75.
Example 14.74.
(The Script)
#!/bin/bash
# Program: opts3
# Using getopts Third try
1
while getopts dq: options
do
case $options in
2 d) echo "d is a valid switch ";;
3 q) echo "The argument for -q is $OPTARG";;
\?) echo "Usage:opts3 -dq filename ... " 1>&2;;
esac
done
(The Command Line)
4 $
opts3 d
d is a valid switch
5 $
opts3 -q foo
The argument for -q is foo
6 $
opts3 -q
Usage:opts3 -dq filename ...
7 $
opts3 e
Usage:opts3 -dq filename ...
8 $
opts3 e
EXPLANATION
-
The
while
command tests the exit status of
getopts
; if
getopts
can successfully process an argument, it returns 0 exit status, and the body of the
while
loop is entered. The colon appended to the argument list means that the
q
option requires an argument. The argument will be stored in the special variable,
OPTARG
.
-
One of the legal options is
d
. If
d
is entered as an option, the
d
(without the dash) is stored in the
options
variable.
-
One of the legal options is
q
. The
q
option requires an argument. There must be a space between the
q
option and its argument. If
q
is entered as an option followed by an argument, the
q
, without the dash, is stored in the
options
variable and the argument is stored in the
OPTARG
variable. If an argument does not follow the
q
option, the question mark is stored in the variable
options
.
-
The
d
option is a legal option to
opts3
.
-
The
q
option with an argument is also a legal option to
opts3
.
-
The
q
option without an argument is an error.
-
The
e
option is invalid. A question mark is stored in the
options
variable if the option is illegal.
-
The option is prepended with
neither
a dash nor a plus sign. The
getopts
command will not process it as an option and returns a nonzero exit status. The
while
loop is
terminated
.
Example 14.75.
(The Script)
#!/bin/bash
# Program: opts4
# Using getopts Fourth try
1
while getopts xyz: arguments 2>/dev/null
do
case $arguments in
2 x) echo "you entered -x as an option .";;
y) echo "you entered -y as an option." ;;
3 z) echo "you entered -z as an option."
echo "$OPTARG is $OPTARG.";;
4 \?) echo "Usage opts4 [-xy] [-z argument]"
exit 1;;
esac
done
5 echo " The number of arguments passed was $(($OPTIND - 1))"
(The Command Line)
$
opts4 -xyz foo
You entered -x as an option.
You entered -y as an option.
You entered -z as an option.
$OPTARG is foo.
The number of arguments passed was 2.
$
opts4 -x -y -z boo
You entered -x as an option.
You entered -y as an option.
You entered -z as an option.
$OPTARG is boo.
The number of arguments passed was 4.
$
opts4 -d
Usage: opts4 [-xy] [-z argument]
EXPLANATION
-
The
while
command tests the exit status of
getopts
; if
getopts
can successfully process an argument, it returns 0 exit status, and the body of the
while
loop is entered. The colon appended to the
z
option tells
getopts
that an argument must follow the
“z
option. If the option takes an argument, the argument is stored in the
getopts
built-in variable
OPTARG
.
-
If
x
is given as an option, it is stored in the variable
arguments
.
-
If
z
is given as an option with an argument, the argument is stored in the built-in variable
OPTARG
.
-
If an invalid option is entered, the question mark is stored in the variable
arguments
and an error message is displayed.
-
The special
getopts
variable
OPTIND
holds the number of the next option to be processed. Its value is always one more than the actual number of command-line arguments.
14.10.2 The
eval
Command and Parsing the Command Line
The
eval
command
evaluates
a command line,
performs
all shell substitutions, and then executes the command line. It is used when normal parsing of the command line is not flexible enough for what we want to do.
Example 14.76.
1 $
set a b c d
2 $
echo The last argument is $$#
3
The last argument is
4 $
eval echo The last argument is $$#
The last argument is d
5 $
set -x
$
eval echo The last argument is $$#
+ eval echo the last argument is ''
++ echo the last argument is d
The last argument is d
EXPLANATION
-
Four positional parameters are set.
-
The desired result is to print the value of the last positional parameter. The
\$
will print a literal dollar sign. The
$#
evaluates to
4
, the number of positional parameters. After the shell evaluates the
$#
, it does not parse the line again to get the value of
$4
.
-
$4
is printed instead of the last argument.
-
After the shell performs variable substitution, the
eval
command performs the variable substitution and then executes the
echo
command.
-
Turn
on the echoing to watch the order of parsing.
Example 14.77.
(From Shutdown Program)
1
eval `/usr/bin/id /usr/bin/sed 's/[^a-z0-9=].*//'`
2 if [ "${uid:=0}" -ne 0 ]
then
3 echo
(From Shutdown Program) 1
eval `/usr/bin/id /usr/bin/sed 's/[^a-z0-9=].*//'`
2 if [ "${uid:=0}" -ne 0 ] then 3 echo $0: Only root can run $0 exit 2 fi
: Only root can run
(From Shutdown Program) 1
eval `/usr/bin/id /usr/bin/sed 's/[^a-z0-9=].*//'`
2 if [ "${uid:=0}" -ne 0 ] then 3 echo $0: Only root can run $0 exit 2 fi
exit 2
fi
EXPLANATION
-
This is a tricky one. The
id
program's output is sent to
sed
to extract the
uid
part of the string. The output for
id
is
uid=9496(ellie) gid=40 groups=40
uid=0(root) gid=1(daemon) groups=1(daemon)
The
sed
regular expression reads: Starting at the beginning of the string, find any character that is not a letter, number, or an equal sign and remove that character and all
characters
following it. The result is to substitute everything from the first opening parenthesis to the end of the line with nothing. What is left is either
uid=9496
or
uid=0.
After
eval
evaluates the command line, it then executes the resulting command:
uid=9496
or
uid=0
.
For example, if the user's ID is
root
, the command executed would be
uid=0
. This creates a local variable in the shell called
uid
and
assigns
0 to it.
-
The value of the
uid
variable is tested for 0, using command modifiers.
-
If the
uid
is not 0, the
echo
command displays the script name (
$0
) and the message.
|