IN THIS CHAPTER
Shell programs become far more useful after you learn how to process arguments passed to them. In this chapter, you'll learn how to write shell programs that take arguments typed on the command line. Recall the program run
that you wrote in Chapter 5, “And Away We Go,” to run the file sys.caps
through tbl
, nroff
, and lp
:
$ cat run
tbl sys.caps | nroff -mm –Tlp | lp
$
Suppose that you need to run other files besides sys.caps
through this same command sequence. You could make a separate version of run
for each such file; or, you could modify the run
program so that you could specify the name of the file to be run on the command line. That is, you could change run
so that you could type
run new.hire
for example, to specify that the file new.hire
is to be run through this command sequence, or
run sys.caps
to specify the file sys.caps
.
Whenever you execute a shell program, the shell automatically stores the first argument in the special shell variable 1
, the second argument in the variable 2
, and so on. These special variables—more formally known as positional parameters—are assigned after the shell has done its normal command-line processing (that is, I/O redirection, variable substitution, filename substitution, and so on).
To modify the run
program to accept the name of the file as an argument, all you do to the program is change the reference to the file sys.caps
so that it instead references the first argument typed on the command line:
$ cat run tbl $1 | nroff -mm -Tlp | lp $ run new.hire Execute it with new.hire as the argument request id is laser1-24 (standard input) $
Each time you execute the run
program, whatever word follows on the command line will be stored inside the first positional parameter by the shell. In the example, new.hire
will be stored in this parameter. Substitution of positional parameters is identical to substitution of other types of variables, so when the shell sees
tbl $1
it replaces the $1
with the first argument supplied to the program: new.hire
.
As another example, the following program, called ison
, lets you know if a specified user is logged on:
$ cat ison who | grep $1 $ who See who's on root console Jul 7 08:37 barney tty03 Jul 8 12:28 fred tty04 Jul 8 13:40 joanne tty07 Jul 8 09:35 tony tty19 Jul 8 08:30 lulu tty23 Jul 8 09:55 $ ison tony tony tty19 Jul 8 08:30 $ ison pat $ Not logged on
Whenever you execute a shell program, the special shell variable $#
gets set to the number of arguments that were typed on the command line. As you'll see in the next chapter, this variable can be tested by the program to determine whether the correct number of arguments was typed by the user.
The next program called args
was written just to get you more familiar with the way arguments are passed to shell programs. Study the output from each example and make sure that you understand it:
$ cat args Look at the program echo $# arguments passed echo arg 1 = :$1: arg 2 = :$2: arg 3 = :$3: $ args a b c Execute it 3 arguments passed arg 1 = :a: arg 2 = :b: arg 3 = :c: $ args a b Try it with two arguments 2 arguments passed arg 1 = :a: arg 2 = :b: arg 3 = :: Unassigned args are null $ args Try it with no arguments 0 arguments passed arg 1 =:: arg 2 =:: arg 3 = :: $ args "a b c" Try quotes 1 arguments passed arg 1 = :a b c: arg 2 = :: arg 3 = :: $ ls x* See what files start with x xact xtra $ args x* Try file name substitution 2 arguments passed arg 1 = :xact: arg 2 = :xtra: arg 3 = :: $ my_bin=/users/steve/bin $ args $my_bin And variable substitution 1 arguments passed arg 1 = :/users/steve/bin: arg 2 = :: arg 3 = :: $ args $(cat names) Pass the contents of names 7 arguments passed arg 1 = :Charlie: arg 2 = :Emanuel: arg3 = :Fred: $
As you can see, the shell does its normal command-line processing even when it's executing your shell programs. This means that you can take advantage of the normal niceties such as filename substitution and variable substitution when specifying arguments to your programs.
The special variable $*
references all the arguments passed to the program. This is often useful in programs that take an indeterminate or variable number of arguments. You'll see some more practical examples later. Here's a program that illustrates its use:
Here's the phonebook
file from previous examples:
$ cat phonebook
Alice Chebba 973-555-2015
Barbara Swingle 201-555-9257
Liz Stachiw 212-555-2298
Susan Goldberg 201-555-7776
Susan Topple 212-555-4932
Tony Iannino 973-555-1295
$
You know how to look up someone in the file by using grep
:
$ grep Cheb phonebook
Alice Chebba 973-555-2015
$
And you know that if you want to look up someone by the full name, you'd better put quotes around it to keep the argument together:
$ grep "Susan T" phonebook
Susan Topple 212-555-4932
$
It would be nice to write a shell program that you could use to look up someone. Let's call the program lu
and have it take as its argument the name of the person to look up:
$ cat lu
#
# Look someone up in the phone book
#
grep $1 phonebook
$
Here's a sample use of lu
:
$ lu Alice Alice Chebba 973-555-2015 $ lu Susan Susan Goldberg 201-555-7776 Susan Topple 212-555-4932 $ lu "Susan T" grep: can't open T phonebook:Susan Goldberg 201-555-7776 phonebook:Susan Topple 212-555-4932 $
In the preceding example, you were careful to enclose Susan T
in double quotes; so what happened? Look again at the grep
executed in the lu
program:
grep $1 phonebook
Even though enclosing Susan T
inside double quotes results in its getting passed to lu
as a single argument, when the shell substitutes this value for $1
on grep
's command line, it then passes it as two arguments to grep
. (Remember we had this same sort of discussion when we talked about variable substitution—first the shell substitutes the value of the variable; then it divides the line into arguments.)
You can alleviate this problem by enclosing $1
inside double quotes (why not single?) in the lu
program:
$ cat lu
#
# Look someone up in the phone book -- version 2
#
grep "$1" phonebook
$
Now let's try it again:
Let's continue with the development of programs that work with the phonebook
file. You'll probably want to add someone to the file, particularly because our phonebook
file is so small. You can write a program called add
that takes two arguments: the name of the person to be added and the number. Then you can simply write the name and number, separated from each other by a tab character, onto the end of the phonebook
file:
$ cat add
#
# Add someone to the phone book
#
echo "$1 $2" >> phonebook
$
Although you can't tell, there's a tab character that separates the $1
from the $2
in the preceding echo
command. This tab must be quoted to make it to echo
without getting gobbled up by the shell.
$ add 'Stromboli Pizza' 973-555-9478 $ lu Pizza See if we can find the new entry Stromboli Pizza 973-555-9478 So far, so good $ cat phonebook See what happened Alice Chebba 973-555-2015 Barbara Swingle 201-555-9257 Liz Stachiw 212-555-2298 Susan Goldberg 201-555-7776 Susan Topple 212-555-4932 Tony Iannino 973-555-1295 Stromboli Pizza 973-555-9478 $
Stromboli Pizza
was quoted so that the shell passed it along to add
as a single argument (what would have happened if it wasn't quoted?). After add
finished executing, lu
was run to see whether it could find the new entry, and it did. The cat
command was executed to see what the modified phonebook
file looked like. The new entry was added to the end, as intended. Unfortunately, the new file is no longer sorted. This won't affect the operation of the lu
program, but you can add a sort
to the add
program to keep the file sorted after new entries are added:
$ cat add
#
# Add someone to the phonebook file -- version 2
#
echo "$1 $2" >> phonebook
sort -o phonebook phonebook
$
Recall that the -o
option to sort specifies where the sorted output is to be written, and that this can be the same as the input file:
$ add 'Billy Bach' 201-555-7618 $ cat phonebook Alice Chebba 973-555-2015 Barbara Swingle 201-555-9257 Billy Bach 201-555-7618 Liz Stachiw 212-555-2298 Stromboli Pizza 973-555-9478 Susan Goldberg 201-555-7776 Susan Topple 212-555-4932 Tony Iannino 973-555-1295 $
So each time a new entry is added, the phonebook
file will get re-sorted.
No set of programs that enable you to look up or add someone to the phone book would be complete without a program to remove someone from the phone book. We'll call the program rem
and have it take as its argument the name of the person to be removed. What should the strategy be for developing the program? Essentially, you want to remove the line from the file that contains the specified name. The -v
option to grep
can be used here because it prints lines from a file that don't match a pattern:
$ cat rem
#
# Remove someone from the phone book
#
grep -v "$1" phonebook > /tmp/phonebook
mv /tmp/phonebook phonebook
$
The grep
writes all lines that don't match into the file /tmp/phonebook
. [1] After the grep
is done, the old phonebook
file is replaced by the new one from /tmp
.
$ rem 'Stromboli Pizza' Remove this entry $ cat phonebook Alice Chebba 973-555-2015 Barbara Swingle 201-555-9257 Billy Bach 201-555-7618 Liz Stachiw 212-555-2298 Susan Goldberg 201-555-7776 Susan Topple 212-555-4932 Tony Iannino 973-555-1295 $ rem Susan $ cat phonebook Alice Chebba 973-555-2015 Barbara Swingle 201-555-9257 Billy Bach 201-555-7618 Liz Stachiw 212-555-2298 Tony Iannino 973-555-1295 $
The first case, where Stromboli Pizza
was removed, worked fine. In the second case, however, both Susan
entries were removed because they both matched the pattern. You can use the add
program to add them back to the phone book:
$ add 'Susan Goldberg' 201-555-7776 $ add 'Susan Topple' 212-555-4932 $
In Chapter 8, “Decisions, Decisions,” you'll learn how to determine whether more than one matching entry is found and take some other action if that's the case. For example, you might want to alert the user that more than one match has been found and further qualification of the name is required. (This can be very helpful, because most implementations of grep
will match everything if an empty string is passed as the pattern.)
Incidentally, before leaving this program, note that sed
could have also been used to delete the matching entry. In such a case, the grep
could be replaced with
sed "/$1/d" phonebook > /tmp/phonebook
to achieve the same result. The double quotes are needed around the sed
command to ensure that the value of $1
is substituted, while at the same time ensuring that the shell doesn't see a command line like
sed /Stromboli Pizza/d phonebook > /tmp/phonebook
If you supply more than nine arguments to a program, you cannot access the tenth and greater arguments with $10
, $11
, and so on. If you try to access the tenth argument by writing
$10
the shell actually substitutes the value of $1
followed by a 0
. Instead, the format
${n}
must be used. So to directly access argument 10, you must write
${10}
The shift
command allows you to effectively left shift your positional parameters. If you execute the command
shift
whatever was previously stored inside $2
will be assigned to $1
, whatever was previously stored in $3
will be assigned to $2
, and so on. The old value of $1
will be irretrievably lost.
When this command is executed, $#
(the number of arguments variable) is also automatically decremented by one:
$ cat tshift Program to test the shift echo $# $* shift echo $# $* shift echo $# $* shift echo $# $* shift echo $# $* shift echo $# $* $ tshift a b c d e 5 a b c d e 4 b c d e 3 c d e 2 d e 1 e 0 $
If you try to shift
when there are no variables to shift (that is, when $#
already equals zero), you'll get an error message from the shell (the error will vary from one shell to the next):
prog: shift: bad number
where prog is the name of the program that executed the offending shift
.
You can shift more than one “place” at once by writing a count immediately after shift
, as in
shift 3
This command has the same effect as performing three separate shifts:
shift shift shift
The shift
command is useful when processing a variable number of arguments. You'll see it put to use when you learn about loops in Chapter 9, “'Round and 'Round She Goes.”
[1] /tmp
is a directory on all Unix systems that anyone can write to. It's used by programs to create “temporary” files. Each time the system gets rebooted, all the files in /tmp
are usually removed.
3.144.106.150