.H 1 "MISCELLANEOUS \|SUPPORTING \|COMMANDS \|AND \|FEATURES"
.P
Shell procedures can make use of any
\*u
command.
The commands described in this section are either used especially frequently
in shell procedures,
or are explicitly designed for such use.
More detailed descriptions of each of these commands can be found in
Section 1 of the \f2U\s-1NIX\s+1 User's Manual\^\fP [5].
.H 2 "Conditional \|Evaluation: \|%test%"
.P
The
%test%
command
evaluates the expression specified by its arguments and, if the
expression is true, returns a zero
exit status; otherwise, a non-zero (false) exit status is returned;
%test%
also returns a non-zero exit status if it has no arguments.
Often it is convenient to use the
%test%
command as the first
command
in the command-list following an %if%
or a %while%.
Shell variables used in
%test%
expressions should be enclosed in double
quotes if there is any chance of their being null or not set.
.P
On some \*u systems, the square brackets (%[%\|%]%) may be used as an
alias to %test%, e.g., %[ %\f2expression\^\fP% ]% has the same effect as
%test %\f2expression\^\fP.
.P
The following is a partial list of the primaries that can
be used to construct a conditional expression:
.VL 15 3
.LI %-r %\f2file\^\fP
\f2true\^\fP if the named file exists and is readable by the user.
.LI %-w %\f2file\^\fP
\f2true\^\fP if the named file exists and is writable by the user.
.LI %-x %\f2file\^\fP
\f2true\^\fP if the named file exists and is executable by the user.
.LI %-s %\f2file\^\fP
\f2true\^\fP if the named file exists and has a size greater than zero.
.LI %-d %\f2file\^\fP
\f2true\^\fP if the named file is a directory.
.LI %-f %\f2file\^\fP
\f2true\^\fP if the named file is an ordinary file.
.LI %-z %\f2s1\^\fP
\f2true\^\fP if the length of string
.I s1\^
is zero.
.LI %-n %\f2s1\^\fP
\f2true\^\fP if the length of the string
.I s1\^
is non-zero.
.LI %-t%~\f2fildes\^\fP
true if the open file whose file descriptor number is \f2fildes\^\fP
is associated with a terminal device.
If \f2fildes\^\fP is not specified, file descriptor %1% is used by default.
.LI \f2s1\^\fP~%=%~\f2s2\^\fP
\f2true\^\fP if strings
.I s1\^
and
.I s2\^
are identical.
.LI \f2s1\^\fP~%!=%~\f2s2\^\fP
\f2true\^\fP if strings
.I s1\^
and
.I s2\^
are
.I not\^
identical.
.LI \f2s1\^\fP
\f2true\^\fP if
.I s1\^
is \f2not\^\fP the null string.
.LI \f2n1\^\fP~%-eq%~\f2n2\^\fP
\f2true\^\fP if the integers
.I n1\^
and
.I n2\^
are algebraically equal;
other algebraic comparisons are indicated by
%-ne%,
%-gt%,
%-ge%,
%-lt%,
and
%-le%.
.LE
.P
These primaries may be combined with the following operators:
.VL 15 3
.LI %!%
unary negation operator.
.LI %-a%
binary logical
.I and\^
operator.
.LI %-o%
binary logical
.I or\^
operator;
it has lower precedence than
%-a%.
.LI %( %\f2expr\^\fP% )%
parentheses for grouping;
they must be escaped
to remove their significance to the shell;
in the absence of parentheses, evaluation proceeds from left to right.
.LE
.P
Note that all primaries, operators, file names, etc. are separate arguments to
%test%.
.H 2 "Reading \|a \|Line: \|%line%"
.P
The
%line%
command takes one line from standard input and prints it
on standard output.
This is useful when you need
to read a line from a file, or capture the line in a variable.
The functions of
%line%
and of the
%read%
command that is internal to the shell
differ in that input/output redirection is possible only with
%line%.
If the user does not require input/output redirection,
%read%
is faster and more efficient.
An example of a usage of
%line%
for which
%read%
would not suffice is:
.CW
firstline=`line < somefile`
.CN
.H 2 "Simple \|Output: \|%echo%"
.P
The
%echo%
command,
invoked as
%echo%~[ \f2arg\^\fP .\|.\|. ]
copies its arguments to the standard output,
each followed by a single space,
except for the last argument, which is normally followed by a new-line;
often, it is used to prompt the user for input,
to issue diagnostics in shell procedures,
or to add a few lines to an output stream in the middle of a pipeline.
Another use is to verify the argument list generation process
before issuing a command that does something drastic.
The command
%ls%
is often replaced by
%echo *%,
because the
latter is faster and prints fewer lines of output.
.P
The %echo% command
recognizes several escape sequences.
A
%\n%
yields a new-line character;
a
%\c%
removes the new-line from the end of the
%echo%ed
line.
The following prompts the user,
allowing one to type on the same line as the prompt:
.CW
echo 'enter name:\c'
read name
.CN
The %echo% command
also recognizes octal escape sequences for \f2all\^\fP characters,
whether printable or not: %echo "\007"% typed at a terminal
will
cause the bell on that terminal to ring.
.H 2 "Expression \|Evaluation: \|%expr%"
.P
The %expr% command
provides arithmetic
and logical operations on integers and
some pattern matching facilities on its arguments.
It evaluates a single expression and writes the result on
the standard output;
%expr%
can be used inside grave accents to set a variable.
Typical examples are:
.CW
#	increment $a
a=`expr $a + 1`
#	put third through last characters of
#	$1 into substring
substring=`expr "$1" : '..\(.*\)'`
#	obtain length of $1
c=`expr "$1" : '.*'`
.CN
.P
The most common uses of
%expr%
are in counting iterations of a loop and in using
its pattern matching capability
to pick apart
strings; see \f2expr\^\fP(1) for more details.
.H 2 "%true% and \|%false%"
.P
The
%true%
and
%false%
commands perform the obvious functions of exiting
with zero and non-zero exit status, respectively.
The %true%
command
is often used to implement an unconditional loop.
.H 2 "More \|about \|Input/Output \|Redirection \|and \|File \|Descriptors"
.P
.in +\w'~\(rh~\|'u
.ti -\w'~\(rh~\|'u
.I
~\(rh~\|Beginners should skip this section on first reading.
.R
.in -\w'~\(rh~\|'u
.H 3 "In-line \|Input \|Documents."
Upon seeing a command line of the form:
.CW -t
\f2command\^\fP << \f2eofstring\^\fP
.CN +t
where
.I eofstring\^
is any arbitrary string,
the shell will take the subsequent lines as the standard input
of
.I command\^
until a line is read consisting only of
.I eofstring .
(By appending a minus (%-%)
to %<<%,
leading spaces and tabs are deleted from each line of the input document
before the shell passes the line to \f2command\^\fP.
.P
The shell creates a temporary file containing the input
document and performs variable and command substitution on
its contents before passing it to the command.
Pattern matching on file names is performed on the arguments of command lines
in command substitutions.
In order to prohibit all substitutions, one may quote any character
of
.I eofstring :
.CW -t
\f2command\^\fP << \e\f2eofstring\^\fP
.CN +t
.P
The in-line input document feature is especially useful
for small amounts of input data, where it
is more convenient to place the data in the shell procedure than to
keep it in a separate file.
For instance, one could type:
.CW
cat <<- xx
	This message will be printed on the
	terminal with leading tabs and spaces
	removed.
xx
.CN
This in-line input document feature is most useful in shell procedures.
See %edfind%, %edlast%, and %mmt% in \(sc6.
Note that in-line input documents may not appear within grave accents.\*F~
.FS
This is a implementation bug that should (and may) be fixed eventually.
.FE
.H 3 "Input/Output Redirection \|Using \|File \|Descriptors."
Above (\(sc3.6.2), we mentioned that a command occasionally directs output to some
file associated with a file descriptor other than %1% or %2%.
In languages
such as C, one can associate output with any file descriptor
by using the
.I write (2)
system call.
The shell provides its own mechanism for creating an output file associated
with a particular file descriptor.
By typing:
.CW -t
\f2fd1\^\fP>&\f2fd2\^\fP
.CN +t
where
.I fd1\^
and
.I fd2\^
are valid file descriptors, one can direct output that would normally
be associated with file descriptor
.I fd1\^
onto
the file associated with
.I fd2 .
The default value for
.I fd1\^
and
.I fd2\^
is %1%.
If, at execution time, no file is associated with
.I fd2 ,
then the redirection is void.
The most common use of this mechanism is that of directing
standard error output to the same file as standard output.
This is accomplished by typing:
.CW -t
\f2command\^\fP 2>&1
.CN +t
If one wanted to redirect both standard output and standard error output
to the same file, one would type:
.CW -t
\f2command\^\fP 1> \f2file\^\fP 2>&1
.CN +t
\f2The order here is significant:\^\fP
first, file descriptor %1% is associated with \f2file\^\fP;
then
file descriptor %2% is associated with the same file as is \f2currently\^\fP
associated with file descriptor %1%.
If the order of the redirections were reversed, standard
error output would go to the terminal, and standard output would go
to
.I file ,
because at the time of the error output redirection, file descriptor %1% still
would have been associated with the terminal.
.P
This mechanism can also be generalized to the redirection of standard \f2input\^\fP.
One could type:
.CW -t
\f2fda\^\fP<&\f2fdb\^\fP
.CN +t
to cause both file descriptors
.I fda\^
and
.I fdb\^
to be associated with the same input
file;
if
.I fda\^
or
.I fdb\^
is not specified, file descriptor %0% is assumed.
Such input redirection is useful for a command that uses two or
more
input sources.
Another use of this notation is for sequential reading and processing of a file.
See %merge% in \(sc6 for an example of use of this feature.
.H 2 "Conditional \|Substitution"
.P
Normally, the shell replaces occurrences of %$%\f2variable\^\fP by
the string value assigned to \f2variable\^\fP, if any.
However, there exists a special
notation to allow conditional substitution, dependent upon
whether the variable is set and/or not null.
By definition, a variable is
.I set\^
if it has \f2ever\^\fP been assigned a value.
The value of a variable
can be the
.I null\^
string, which may be assigned to a variable in any one of the
following ways:
.CW
A=
bcd=""
Ef_g=''
set '' ""
.CN
The first three of these examples assign \f2null\^\fP to each of the
corresponding shell variables.
The last example sets the first
\f2and\^\fP second positional parameters to \f2null\^\fP.
The following conditional expressions depend upon
whether a variable is \f2set and not null\^\fP.
Note that the meaning of braces in these expressions differs from that
described
in \(sc4.4.7;
.I parameter\^
as used below refers to either a digit or a variable name:
.VL 23 3
.LI %${%\f2variable\^\fP%:-%\f2string\^\fP%}%
If
.I variable\^
is set and is
non-null,
then substitute the value %$%\f2variable\^\fP
in
place of this expression.
Otherwise, replace the expression with \f2string\^\fP.
Note that
the value of
.I variable\^
is \f2not\^\fP changed by the evaluation of this expression.
.LI %${%\f2variable\^\fP%:=%\f2string\^\fP%}%
If
.I variable\^
is set and is non-null, then substitute the value %$%\f2variable\^\fP in place
of this expression; otherwise, set \f2variable\^\fP to \f2string\^\fP, and then
substitute the value %$%\f2variable\^\fP in place of this expression.
Positional parameters may not be assigned values in this fashion.
.LI %${%\f2variable\^\fP%:?%\f2string\^\fP%}%
If
.I variable\^
is set and is
non-null,
then substitute the value of \f2variable\^\fP for the expression;
otherwise,
print a message
of the form:
.CW -t
\f2variable\^\fP:	\f2string\^\fP
.CN +t
and exit from the current shell.
(If the shell is the login shell, it is
not exited.)~
If
.I string\^
is omitted in this form, then the message:
.CW -t
\f2variable\^\fP:	parameter null or not set
.CN +t
is printed instead.
.LI %${%\f2variable\^\fP%:+%\f2string\^\fP%}%
If
.I variable\^
is set and is non-null,
then substitute
.I string\^
for this expression, otherwise, substitute the null string.
Note that the value of
.I variable\^
is not altered by the evaluation of this expression.
.LE
.P
These expressions may also be used without
the colon (%:%).
In this variation, the shell does \f2not\^\fP check whether
.I variable\^
is
.I null\^
or not; it only checks whether
.I variable\^
has \f2ever\^\fP been set.
.P
The two examples below illustrate the use of this
facility:
.AL 1 6
.LI
If %PATH% has ever been set and is not null,
then keep its current value;
otherwise, set it to the string %:/bin:/usr/bin%.
Note
that one needs an explicit assignment to set %PATH% in this form:
.CW
PATH=${PATH:-':/bin:/usr/bin'}
.CN
.LI
If %HOME% is set and is not null, then change directory to it,
otherwise set it to the given value and change directory to it; note that %HOME% is
automatically assigned a value in this case:
.CW
cd ${HOME:='/usr/gas'}
.CN
.LE
.H 2 "Invocation \|Flags"
.P
There are four flags that may be specified on the command line
invoking the shell;
these flags may \f2not\^\fP be turned on via the %set% command:
.VL 9 3
.LI %-i%
If this flag is specified,
or if the shell's input and output are both attached
to a terminal, the shell is
.I interactive .
In such a shell, %INTERRUPT% (signal 2) is caught and ignored, and
%TERMINATE% (signal 15) and %QUIT% (signal 3) are ignored.
.LI %-s%
If this flag
is specified or if no input/output redirection arguments are given, the
shell reads commands from standard input.
Shell output is written to
file descriptor 2.
The shell you get upon logging into the system
effectively has the
%-s%
flag turned on.
.LI %-c%
When this flag is turned on,
the shell reads commands from the first string
following the flag.
Remaining arguments are ignored.
Double quotes should
be used to enclose a multi-word string, in order to allow for variable
substitution.
.LI %-r%
When this flag
is specified on invocation, then the
.I "restricted shell\^"
is invoked.
This is a version of the shell in which
certain actions are disallowed.
In particular, the
%cd%
command produces an error message, and the user cannot set
%PATH%.
See
.I rsh (1)
for a more detailed description.
.LE
.if t .bp
.H 1 "EXAMPLES \|OF \|SHELL \|PROCEDURES"
.P
.in +\w'~\(rh~\|'u
.ti -\w'~\(rh~\|'u
.I
~\(rh~\|Some examples in this section are
quite difficult for beginners.
For ease of reference, the examples are arranged alphabetically by name.
.R
.in -\w'~\(rh~\|'u
.HU "copypairs:"
.so ex1/copypairs
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This procedure illustrates the use of a %while% loop to process a
list of positional parameters that are somehow related to one another.
Here a %while% loop is much better than a %for% loop, because you
can adjust the positional parameters via %shift% to handle related arguments.
.in -\nnu
.HU "copyto:"
.so ex1/copyto
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This procedure uses an %if% command with several parts in order to
screen out improper usage.
The %for% loop at the end of the procedure
loops over all of the arguments to %copyto% but the first; the original %$1%
is shifted off.
.in -\nnu
.HU "distinct1:"
.so ex1/distinct1
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This procedure is an example of the kind of process
that is created by the
left-to-right
construction of a long pipeline.
It may not be immediately obvious how this works.
(You may wish to consult \f2tr\^\fP(1), \f2sort\^\fP(1), and \f2uniq\^\fP(1) if
you are completely unfamiliar with these commands.)~
The
%tr%
translates all characters except letters and digits
into new-line characters, and then
squeezes out
repeated new-line characters.
This leaves each string (in this case,
any contiguous sequence of letters and digits)
on a separate line.
The %sort% command
sorts the lines and emits only one line from
any sequence of one or more repeated lines.
The next
%tr%
converts everything to lower case,
so that identifiers differing only in case become identical.
The output is sorted again to bring such duplicates together.
The
%uniq -d%
prints (once) only those lines that occur more than once,
yielding the desired list.
.P
The process of building such a pipeline
uses the fact that pipes and files
can usually be interchanged;
the two lines below are equivalent,
assuming that sufficient disk space is available:
.CW
cmd1 | cmd2 | cmd3
cmd1 > temp1; < temp1 cmd2 > temp2; < temp2 cmd3; rm temp[123]
.CN
Starting with a file of test data
on the standard input
and working from left to right,
each command is executed taking its input from the previous file and
putting its output in the next file.
The final output is then examined to make sure that it contains the expected
result.
The goal is to create a series of
transformations that will convert the input to the desired output.
As an exercise, try to mimic
%distinct1%
with such a step-by-step process,
using a file of test data containing:
.CW
ABC:DEF/DEF
ABC1 ABC
Abc abc
.CN
.P
Although pipelines can give a concise notation for complex processes,
exercise some restraint, lest you succumb to the
``one-line syndrome'' sometimes found among users of
especially concise languages.
This syndrome often yields incomprehensible code.
.in -\nnu
.HU "draft:"
.so ex1/draft
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~Users often write this kind of procedure for convenience in
dealing with commands that require the use of many distinct
flags that cannot be given default values that are reasonable for all (or even most) users.
.in -\nnu
.HU "edfind:"
.so ex1/edfind
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This illustrates the practice of using
editor (%ed%)
in-line input scripts
into which the shell can substitute the
values of variables.
It is a good idea to turn on the %H% option
of
%ed%
when embedding an
%ed%
script in a shell procedure (see \f2ed\^\fP(1)).
.in -\nnu
.HU "edlast:"
.so ex1/edlast
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This procedure illustrates the effect of inhibiting substitution by
escaping a character in the
.I eofstring\^
(here, %eof%)
of the input redirection.
If this had not been done, %$p% and %$d% would
have been treated as shell variables.
.in -\nnu
.if t .bp
.HU "fsplit:"
.so ex1/fsplit
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~Each iteration of the loop reads
a line from the input and analyzes it.
The
loop terminates
only when
%read% encounters an end-of-file.
.in -\nnu
.P
.in +\w'~\(rh~\|'u
.ti -\w'~\(rh~\|'u
.I
~\(rh~\|Don't use the shell to read a line at a time unless you must\-it can
be grotesquely slow
.R
(\(sc7.2.1).
.in -\w'~\(rh~\|'u
.HU "initvars:"
.so ex1/initvars
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This shell procedure would be invoked by a user at the terminal, or
as part of a
%.profile%
file.
The assignments are effective even when the procedure is finished, because
the
.I dot\^
command is used to invoke it.
To better understand the \f2dot\^\fP command,
invoke %initvars% as indicated above
and check the values of %HOME%, %PATH%, and %TERM%;
then make %initvars% executable,
type %initvars%, assigning different values to the three variables,
and check again the values of these three shell variables after
%initvars% terminates.
.in -\nnu
.if t .bp
.HU "merge:"
.so ex1/merge
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This command illustrates a technique for reading sequential lines from
a file or files without creating any sub-shells to do so.
When the
file descriptor is used to access a file, the effect is that of opening the file
and moving a file pointer along until the end of the file
is read.
If the input redirections used %src1% and %src2% explicitly
rather than the associated
file descriptors, this procedure would never terminate,
because the \f2first\^\fP line
of each file would be read over and over again.
.in -\nnu
.HU "mkfiles:"
.so ex1/mkfiles
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~The %mkfiles% procedure uses input/output redirection to create
zero-length files.
The %expr% command is used for
counting iterations of the
%while% loop.
.in -\nnu
.if t .bp
.HU "mmt:"
.so ex1/mmt
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This is a slightly simplified version of an actual \*u command.
It uses many of the features available
in the shell.
If you can follow through this procedure without getting lost, you
have a good understanding of shell programming.
Pay particular attention to the process of building a command
line through shell variables and then using
%eval%
to execute it.
.in -\nnu
.HU "null:"
.so ex1/null
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This procedure
uses the fact that output redirection creates the (empty) output file
if that file does not already exist.
.in -\nnu
.HU "phone:"
.so ex1/phone
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~This procedure is an example of using an in-line input
script to maintain a \f2small\^\fP data base.
.in -\nnu
.HU "writemail:"
.so ex1/writemail
.P
.in +\nnu
.ti -\nnu
\f2Note:\^\fP~~Command grouping is illustrated by %writemail%.
The
message specified by %$1% is piped to both the %write% command and,
if %write% fails, to the %mail% command.
.in -\nnu
.nr Hu 1
.H 1 "EFFECTIVE \|AND \|EFFICIENT \|SHELL \|PROGRAMMING"
.H 2 "Overall \|Approach"
.P
This section outlines strategies for writing
.I efficient\^
shell procedures,
i.e., ones that do not waste resources unreasonably in
accomplishing their purposes.
In the authors' opinion,
the primary reason for choosing the shell procedure
as the implementation
method is to achieve a desired result at a minimum
.I human\^
cost.
Emphasis should
.I always\^
be placed on simplicity,
clarity,
and readability,
but efficiency can also be gained through awareness of a few
design strategies.
In many cases, an effective redesign
of an existing procedure improves its efficiency by reducing its size,
and often increases its comprehensibility.
In any case, one should not worry about optimizing shell procedures unless they
are intolerably slow or are known to consume a lot of resources.
.P
The same kind of iteration cycle
should be applied to shell procedures as to other programs:
write code, measure it, and optimize only the \f2few\^\fP important parts.
The user should become familiar with the
%time%
command,
which can be used to measure both entire procedures and parts thereof.
Its use is strongly recommended;
human intuition is notoriously unreliable when used to estimate
timings of programs,
even when the style of programming is a familiar one.
Each timing test should be run several times,
because the results are easily disturbed by, for instance, variations in system load.
.H 2 "Approximate \|Measures \|of \|Resource \|Consumption"
.H 3 "Number \|of \|Processes \|Generated."
When large numbers of short commands are executed,
the actual execution time of the commands may well be dominated by
the overhead of creating processes.
The procedures that incur
significant amounts of
such overhead are those that perform much looping
and those that generate command sequences to be interpreted
by another shell.
.P
If you are worried about
efficiency,
it is important to know which commands
are currently built into the shell, and which are not.
Here is the alphabetical list of those that are built-in:
.CW -t
.ta 1i 2i 3i 4i 5i
break	case	cd	continue	eval
exec	exit	export	for	if
newgrp	read	readonly	set	shift
test	times	trap	umask	until
wait	while	.	:	{}
.CN +t
.P
%()%
executes as a child process, i.e., the shell does a
.I fork ,
but no
.I exec .
Any command
.I not\^
in the above list requires
both
.I fork\^
and
.I exec .
.P
The user should always have at least a vague idea of the number of
processes generated by a shell procedure.
In the bulk of observed procedures,
the number of processes spawned (not necessarily simultaneously) can be described by:
.DS 1
processes = k\^\(**\^n~+~c
.DE
where
.I k\^
and
.I c\^
are constants,
and
.I n\^
is the number of procedure arguments,
the number of lines in some input file,
the number of entries in some directory,
or some other obvious quantity.
Efficiency improvements are most commonly gained by reducing the value of
.I k ,
sometimes to zero.
Any procedure whose complexity measure includes
.I n\^ \v'-.4m'\s-32\s+3\v'.4m'
terms or higher powers of
.I n\^
is likely to be
intolerably expensive.
.P
As an example, here is an analysis
of procedure
%fsplit%
of \(sc6.
For each iteration of the loop,
there is one
%expr%
plus either an
%echo%
or another
%expr%.
One additional
%echo%
is executed at the end.
If
.I n\^
is the number of lines of input,
the number of processes is 2\^\(**\^n\|\^+\|1.
On the other hand,
the number of processes in the following (equivalent) procedure is
12, regardless of the number of lines of input:
.so ex1/fsplit2
This version is
often ten times faster than
%fsplit%,
and it is even faster for larger input files.
.P
Some types of procedures should
.I not\^
be written using the shell.
For example, if
one or more processes are generated for each character in some file,
it is a good indication that the procedure should be rewritten in C.
.P
.in +\w'~\(rh~\|'u
.ti -\w'~\(rh~\|'u
.I
~\(rh~\|Shell procedures should not be used to scan or build files a character at a time.
.R
.in -\w'~\(rh~\|'u
.H 3 "Number \|of \|Data \|Bytes \|Accessed."
It is worthwhile considering any action that reduces the number of bytes
read or written.
This may be important for those
procedures whose time is spent passing data around among a few processes,
rather than in creating large numbers of short processes.
Some filters shrink their output,
others usually increase it.
It always pays to
put the
.I shrinkers\^
first when the order is irrelevant.
Which of the following is likely to be faster?
.CW
sort file | grep pattern
grep pattern file | sort
.CN
.H 3 "Directory \|Searches."
Directory searching can consume a great deal of time,
especially in those applications that
utilize deep directory structures and long path names.
Judicious use of
%cd%
can help shorten long path names and thus reduce the number of
directory searches needed.
As an exercise, try the following commands (on a fairly quiet system):\*F
.FS
You may have to do some reading in the
\f2U\s-1NIX\s+1 User's Manual\^\fP [5] to understand exactly what is going on
in these examples.
.FE
.CW
time sh -c 'ls -l /usr/bin/* >/dev/null'
time sh -c 'cd /usr/bin; ls -l * >/dev/null'
.CN
.H 2 "Efficient \|Organization"
.H 3 "Directory-Search \|Order \|and \|the \|%PATH% \|Variable."
The
%PATH%
variable
is a
convenient mechanism
for allowing organization and sharing of procedures.
However, it must be used in a sensible fashion,
or the result may be a great increase in system overhead that
occurs in a subtle, but avoidable, way.
.P
The process of finding a command involves reading every directory
included in every path name that precedes the needed path name in the
current %PATH% variable.
As an example, consider the effect of invoking
%nroff%
(i.e., %/usr/bin/nroff%) when %$PATH% is
%:/bin:/usr/bin%.
The sequence of directories read is: %.%,
%/%,
%/bin%,
%/%,
%/usr%,
and
%/usr/bin%,
i.e., a total of six directories.
A long
path list assigned to %PATH%
can increase this number significantly.
.P
The vast majority of command executions are of commands found in
%/bin%
and, to a somewhat lesser extent, in
%/usr/bin%.
Careless
%PATH%
setup may lead to
a great deal of unnecessary searching.
The following four examples are ordered from worst to best
(but
.I only\^
with respect to the efficiency of command searches):
.CW
:/a1/tf/jtb/bin:/usr/lbin:/bin:/usr/bin
:/bin:/a1/tf/jtb/bin:/usr/lbin:/usr/bin
:/bin:/usr/bin:/a1/tf/jtb/bin:/usr/lbin
/bin::/usr/bin:/a1/tf/jtb/bin:/usr/lbin
.CN
.P
The first one above should be avoided.
The others are acceptable, the choice among them is
dictated by the rate of change in the set of commands
kept in
%/bin%
and
%/usr/bin%.
.P
A procedure that is expensive because it invokes many
short-lived commands may often be speeded up by
%set%ting the %PATH% variable inside the procedure such that the
fewest possible directories are searched in an optimum order; the
%mmt% example in \(sc6 does this.
.H 3 "Good \|Ways \|to \|Set \|Up \|Directories."
It is wise to avoid directories that are larger than necessary.
You should be aware of several
.I magic\^
sizes.
A directory that contains entries for up to 30 files (plus the required
%.%
and
\&%..%)
fits in a single disk block and can be searched very efficiently.
One that has up to 286 entries is still a
.I small\^
file;
anything larger is usually a disaster when used as a working directory.
It is especially important to keep
login directories small, preferably one block at most.
Note that, as a rule, directories never shrink.
.HU "ACKNOWLEDGEMENTS"
.P
The \*u shell was initially written by S. R. Bourne [2].
Its design is based, in part, on the original \*u shell [13]
and on the \s-1PWB/UNIX\s+1 shell [10], some features having been taken from both.
Similarities also exist with the command interpreters
of the Cambridge Multiple Access System
and of the \s-1MIT\s+1 Compatible Time-Sharing System.
Several colleagues provided helpful comments during the
writing of this tutorial; T. A. Dolotta, in addition, provided a great
deal of editorial assistance.
.if t .bp
.HU "REFERENCES"
.S -1
.RL
.LI
Bianchi, M. H., and Wood, J. L.
A User's Viewpoint on the Programmer's Workbench.
.I "Proc. Second Int. Conf. on Software Engineering,\^"
pp.~193-99 (Oct. 13-15, 1976).
.LI
Bourne, S. R.
The U\s-1NIX\s+1 Shell.
.I "The Bell System Technical Journal,\^"
Vol. 57, No. 6, Part 2, pp. 1971-90
(July-Aug. 1978).
.LI
Dolotta, T. A., and Mashey, J. R.
An Introduction to the Programmer's Workbench.
.I "Proc. Second Int. Conf. on Software Engineering,\^"
pp.~164-68 (Oct. 13-15, 1976).
.LI
Dolotta, T. A., and Mashey, J. R.
Using a Command Language as the Primary Programming Tool.
In: Beech, D. (ed.),
.I "Command Language Directions\^"
(Proc. of the Second \s-1IFIP\s+1 Working Conf. on Command Languages).
Amsterdam: North Holland (1980).
.LI
Dolotta, T. A., Olsson, S. B., and Petruccelli, A. G., eds.
.I "U\s-1NIX\s+1 User's Manual,\^"
Edition~3.0.
Bell Laboratories (June 1980).
.LI
Kernighan, B. W., and Mashey, J. R.
The U\s-1NIX\s+1 Programming Environment.
\f2Software\-Practice & Experience\^\fP, Vol.~9, No.~1, pp.~1-15 (Jan. 1979).
.LI
Kernighan, B. W., and Plauger, P. J.
Software Tools.
.I "Proc. First Nat. Conf. on Software Engineering,\^"
pp.~8-13 (Sept. 11-12, 1975).
.LI
Kernighan, B. W., and Plauger, P. J.
.I "Software Tools.\^~"
Reading, \s-1MA\s+1: Addison-Wesley (1976).
.LI
Kernighan, B. W., and Ritchie, D. M.
.I "The C Programming Language.\^~"
Englewood Cliffs, \s-1NJ\s+1: Prentice-Hall (1978).
.LI
Mashey, J. R.
\f2P\s-1WB/UNIX\s+1 Shell Tutorial\^\fP.
Bell Laboratories (1977).
.LI
Ritchie, D. M., and Thompson, K.
The U\s-1NIX\s+1 Time-Sharing System.
.I "The Bell System Technical Journal,\^"
Vol. 57, No. 6, Part 2, pp. 1905-29 (July-Aug. 1978).
.LI
Snyder, G. A., and Mashey, J. R.
U\s-1NIX\s+1 Documentation Road Map.
Internal report, Bell Laboratories (Nov. 1979).
.LI
Thompson, K.
The U\s-1NIX\s+1 Command Language.
In:
.I "Structured Programming\(emInfotech State of the Art Report,\^"
pp.~375-84.
Infotech International Limited, Nicholson House, Maidenhead, Berkshire, England (1976).
.LE
.S
.sp 1v
.I "May 1980"
.br
.pl +.5v
.nr L +.5v
.TC 1 1 1 0
