d90bee9749
/etc/profile enables by default tabcompletion, as well as emacs mode, in order to keep the old MINIX ash behavior. Note: The shell now refuses to source a script without a relative or absolute path. This means: - '. myscript.sh' fails, while - '. ./myscript.sh' succeeds Change-Id: I0be89b0747bd005e4c05cadb937af86883627dc6
971 lines
23 KiB
Text
971 lines
23 KiB
Text
.\" $NetBSD: t2,v 1.3 2010/08/22 02:19:07 perry Exp $
|
|
.\"
|
|
.\" Copyright (C) Caldera International Inc. 2001-2002. All rights reserved.
|
|
.\"
|
|
.\" Redistribution and use in source and binary forms, with or without
|
|
.\" modification, are permitted provided that the following conditions are
|
|
.\" met:
|
|
.\"
|
|
.\" Redistributions of source code and documentation must retain the above
|
|
.\" copyright notice, this list of conditions and the following
|
|
.\" disclaimer.
|
|
.\"
|
|
.\" Redistributions in binary form must reproduce the above copyright
|
|
.\" notice, this list of conditions and the following disclaimer in the
|
|
.\" documentation and/or other materials provided with the distribution.
|
|
.\"
|
|
.\" All advertising materials mentioning features or use of this software
|
|
.\" must display the following acknowledgment:
|
|
.\"
|
|
.\" This product includes software developed or owned by Caldera
|
|
.\" International, Inc. Neither the name of Caldera International, Inc.
|
|
.\" nor the names of other contributors may be used to endorse or promote
|
|
.\" products derived from this software without specific prior written
|
|
.\" permission.
|
|
.\"
|
|
.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
|
|
.\" INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
|
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
.\" DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE LIABLE
|
|
.\" FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
.\" OR OTHERWISE) RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
.\"
|
|
.\" @(#)t2 8.1 (Berkeley) 6/8/93
|
|
.\"
|
|
.SH
|
|
2.0\ Shell\ scripts
|
|
.LP
|
|
The shell may be used to read and execute commands
|
|
contained in a file.
|
|
For example,
|
|
.DS
|
|
sh file [ args \*(ZZ ]
|
|
.DE
|
|
calls the shell to read commands from \fIfile.\fP
|
|
Such a file is called a \fIshell script.\fP
|
|
Arguments may be supplied with the call
|
|
and are referred to in \fIfile\fP
|
|
using the positional parameters
|
|
\fB$1, $2, \*(ZZ\|.\fR
|
|
.LP
|
|
For example, if the file \fIwg\fP contains
|
|
.DS
|
|
who \*(VT grep $1
|
|
.DE
|
|
then
|
|
.DS
|
|
sh wg fred
|
|
.DE
|
|
is equivalent to
|
|
.DS
|
|
who \*(VT grep fred
|
|
.DE
|
|
.LP
|
|
UNIX files have three independent attributes,
|
|
\fIread,\fP \fIwrite\fP and \fIexecute.\fP
|
|
The UNIX command \fIchmod\fP(1) may be used
|
|
to make a file executable.
|
|
For example,
|
|
.DS
|
|
chmod +x wg
|
|
.DE
|
|
will ensure that the file \fIwg\fP has execute status.
|
|
Following this, the command
|
|
.DS
|
|
wg fred
|
|
.DE
|
|
is equivalent to
|
|
.DS
|
|
sh wg fred
|
|
.DE
|
|
This allows shell scripts and other programs
|
|
to be used interchangeably.
|
|
In either case a new process is created to
|
|
run the command.
|
|
.LP
|
|
The `\fB#\fP' character is used as a comment character by the shell.
|
|
All characters following the `#' on a line are ignored.
|
|
.LP
|
|
A typical modern system has several different shells, some with differing
|
|
command syntax, and it is desirable to specify which one should be
|
|
invoked when an executable script is invoked.
|
|
If the special comment
|
|
.DS
|
|
#!/\fIpath\fP/\fIto\fP/\fIinterpreter\fP
|
|
.DE
|
|
appears as the first line in a script, it is used to specify the
|
|
absolute pathname of the shell (or other interpreter) that should be
|
|
used to execute the file.
|
|
(Without such a line, \fB/bin/sh\fP is assumed.)
|
|
It is best if a script explicitly states
|
|
what shell it is intended for in this manner.
|
|
.LP
|
|
As well as providing names for the positional
|
|
parameters,
|
|
the number of positional parameters to a script
|
|
is available as \fB$#\|.\fP
|
|
The name of the file being executed
|
|
is available as \fB$0\|.\fP
|
|
.LP
|
|
A special shell parameter \fB$\*(ST\fP
|
|
is used to substitute for all positional parameters
|
|
except \fB$0\|.\fP
|
|
A typical use of this is to provide
|
|
some default arguments,
|
|
as in,
|
|
.DS
|
|
nroff \(miT450 \(mims $\*(ST
|
|
.DE
|
|
which simply prepends some arguments
|
|
to those already given.
|
|
(The variable \fB$@\fP also expands to ``all positional
|
|
parameters'', but is subtly different when expanded inside quotes.
|
|
See section 3.5, below.)
|
|
.SH
|
|
2.1\ Control\ flow\ -\ for
|
|
.LP
|
|
A frequent use of shell scripts is to loop
|
|
through the arguments (\fB$1, $2, \*(ZZ\fR)
|
|
executing commands once for each argument.
|
|
An example of such a script is
|
|
\fItel\fP that searches the file
|
|
\fB/usr/share/telnos\fR
|
|
that contains lines of the form
|
|
.DS
|
|
\*(ZZ
|
|
fred mh0123
|
|
bert mh0789
|
|
\*(ZZ
|
|
.DE
|
|
The text of \fItel\fP is
|
|
.DS
|
|
#!/bin/sh
|
|
|
|
for i
|
|
do
|
|
grep $i /usr/share/telnos
|
|
done
|
|
.DE
|
|
The command
|
|
.DS
|
|
tel fred
|
|
.DE
|
|
prints those lines in \fB/usr/share/telnos\fR
|
|
that contain the string \fIfred\|.\fP
|
|
.DS
|
|
tel fred bert
|
|
.DE
|
|
prints those lines containing \fIfred\fP
|
|
followed by those for \fIbert.\fP
|
|
.LP
|
|
The \fBfor\fP loop notation is recognized by the shell
|
|
and has the general form
|
|
.DS
|
|
\fBfor\fR \fIname\fR \fBin\fR \fIw1 w2 \*(ZZ\fR
|
|
\fBdo\fR \fIcommand-list\fR
|
|
\fBdone\fR
|
|
.DE
|
|
A \fIcommand-list\fP is a sequence of one or more
|
|
simple commands separated or terminated by a newline or semicolon.
|
|
Furthermore, reserved words
|
|
like \fBdo\fP and \fBdone\fP are only
|
|
recognized following a newline or
|
|
semicolon.
|
|
\fIname\fP is a shell variable that is set
|
|
to the words \fIw1 w2 \*(ZZ\fR in turn each time the \fIcommand-list\fP
|
|
following \fBdo\fP
|
|
is executed.
|
|
If \fBin\fR \fIw1 w2 \*(ZZ\fR
|
|
is omitted then the loop
|
|
is executed once for each positional parameter;
|
|
that is, \fBin\fR \fI$\*(ST\fR is assumed.
|
|
.LP
|
|
Another example of the use of the \fBfor\fP
|
|
loop is the \fIcreate\fP command
|
|
whose text is
|
|
.DS
|
|
for i do >$i; done
|
|
.DE
|
|
The command
|
|
.DS
|
|
create alpha beta
|
|
.DE
|
|
ensures that two empty files
|
|
\fIalpha\fP and \fIbeta\fP exist
|
|
and are empty.
|
|
The notation \fI>file\fP may be used on its
|
|
own to create or clear the contents of a file.
|
|
Notice also that a semicolon (or newline) is required before \fBdone.\fP
|
|
.SH
|
|
2.2\ Control\ flow\ -\ case
|
|
.LP
|
|
A multiple way branch is provided for by the
|
|
\fBcase\fP notation.
|
|
For example,
|
|
.DS
|
|
case $# in
|
|
\*(Ca1) cat \*(AP$1 ;;
|
|
\*(Ca2) cat \*(AP$2 <$1 ;;
|
|
\*(Ca\*(ST) echo \'usage: append [ from ] to\' ;;
|
|
esac
|
|
.DE
|
|
is an \fIappend\fP command.
|
|
When called
|
|
with one argument as
|
|
.DS
|
|
append file
|
|
.DE
|
|
\fB$#\fP is the string \fI1\fP and
|
|
the standard input is copied onto the
|
|
end of \fIfile\fP
|
|
using the \fIcat\fP command.
|
|
.DS
|
|
append file1 file2
|
|
.DE
|
|
appends the contents of \fIfile1\fP
|
|
onto \fIfile2.\fP
|
|
If the number of arguments supplied to
|
|
\fIappend\fP is other than 1 or 2
|
|
then a message is printed indicating
|
|
proper usage.
|
|
.LP
|
|
The general form of the \fBcase\fP command
|
|
is
|
|
.DS
|
|
\fBcase \fIword \fBin
|
|
\*(Ca\fIpattern\|\fB)\ \fIcommand-list\fB\|;;
|
|
\*(Ca\*(ZZ
|
|
\fBesac\fR
|
|
.DE
|
|
The shell attempts to match
|
|
\fIword\fR with each \fIpattern,\fR
|
|
in the order in which the patterns
|
|
appear.
|
|
If a match is found the
|
|
associated \fIcommand-list\fP is
|
|
executed and execution
|
|
of the \fBcase\fP is complete.
|
|
Since \*(ST is the pattern that matches any
|
|
string it can be used for the default case.
|
|
.LP
|
|
A word of caution:
|
|
no check is made to ensure that only
|
|
one pattern matches
|
|
the case argument.
|
|
The first match found defines the set of commands
|
|
to be executed.
|
|
In the example below the commands following
|
|
the second \*(ST will never be executed.
|
|
.DS
|
|
case $# in
|
|
\*(Ca\*(ST) \*(ZZ ;;
|
|
\*(Ca\*(ST) \*(ZZ ;;
|
|
esac
|
|
.DE
|
|
.LP
|
|
Another example of the use of the \fBcase\fP
|
|
construction is to distinguish
|
|
between different forms
|
|
of an argument.
|
|
The following example is a fragment of a \fIcc\fP command.
|
|
.DS
|
|
for i
|
|
do case $i in
|
|
\*(DC\(mi[ocs]) \*(ZZ ;;
|
|
\*(DC\(mi\*(ST) echo "unknown flag $i" ;;
|
|
\*(DC\*(ST.c) /lib/c0 $i \*(ZZ ;;
|
|
\*(DC\*(ST) echo "unexpected argument $i" ;;
|
|
\*(DOesac
|
|
done
|
|
.DE
|
|
.LP
|
|
To allow the same commands to be associated
|
|
with more than one pattern
|
|
the \fBcase\fP command provides
|
|
for alternative patterns
|
|
separated by a \*(VT\|.
|
|
For example,
|
|
.DS
|
|
case $i in
|
|
\*(Ca\(mix\*(VT\(miy) \*(ZZ
|
|
esac
|
|
.DE
|
|
is equivalent to
|
|
.DS
|
|
case $i in
|
|
\*(Ca\(mi[xy]) \*(ZZ
|
|
esac
|
|
.DE
|
|
.LP
|
|
The usual quoting conventions apply
|
|
so that
|
|
.DS
|
|
case $i in
|
|
\*(Ca\\?) \*(ZZ
|
|
.DE
|
|
will match the character \fB?\|.\fP
|
|
.SH
|
|
2.3\ Here\ documents
|
|
.LP
|
|
The shell script \fItel\fP
|
|
in section 2.1 uses the file \fB/usr/share/telnos\fR
|
|
to supply the data
|
|
for \fIgrep.\fP
|
|
An alternative is to include this
|
|
data
|
|
within the shell script as a \fIhere\fP document, as in,
|
|
.DS
|
|
for i
|
|
do grep $i \*(HE!
|
|
\*(DO\*(ZZ
|
|
\*(DOfred mh0123
|
|
\*(DObert mh0789
|
|
\*(DO\*(ZZ
|
|
!
|
|
done
|
|
.DE
|
|
In this example
|
|
the shell takes the lines between \fB\*(HE!\fR and \fB!\fR
|
|
as the standard input for \fIgrep.\fP
|
|
The string \fB!\fR is arbitrary, the document
|
|
being terminated by a line that consists
|
|
of the string following \*(HE\|.
|
|
.LP
|
|
Parameters are substituted in the document
|
|
before it is made available to \fIgrep\fP
|
|
as illustrated by the following script
|
|
called \fIedg\|.\fP
|
|
.DS
|
|
ed $3 \*(HE%
|
|
g/$1/s//$2/g
|
|
w
|
|
%
|
|
.DE
|
|
The call
|
|
.DS
|
|
edg string1 string2 file
|
|
.DE
|
|
is then equivalent to the command
|
|
.DS
|
|
ed file \*(HE%
|
|
g/string1/s//string2/g
|
|
w
|
|
%
|
|
.DE
|
|
and changes all occurrences of \fIstring1\fP
|
|
in \fIfile\fP to \fIstring2\|.\fP
|
|
Substitution can be prevented using \\
|
|
to quote the special character \fB$\fP
|
|
as in
|
|
.DS
|
|
ed $3 \*(HE+
|
|
1,\\$s/$1/$2/g
|
|
w
|
|
+
|
|
.DE
|
|
(This version of \fIedg\fP is equivalent to
|
|
the first except that \fIed\fP will print
|
|
a \fB?\fR if there are no occurrences of
|
|
the string \fB$1\|.\fP)
|
|
Substitution within a \fIhere\fP document
|
|
may be prevented entirely by quoting
|
|
the terminating string,
|
|
for example,
|
|
.DS
|
|
grep $i \*(HE'end'
|
|
\*(ZZ
|
|
end
|
|
.DE
|
|
The document is presented
|
|
without modification to \fIgrep.\fP
|
|
If parameter substitution is not required
|
|
in a \fIhere\fP document this latter form
|
|
is more efficient.
|
|
.SH
|
|
2.4\ Shell\ variables\(dg
|
|
.LP
|
|
.FS
|
|
Also known as \fIenvironment variables\fB, see \fIenvironment\fB(7).
|
|
.FE
|
|
The shell
|
|
provides string-valued variables.
|
|
Variable names begin with a letter
|
|
and consist of letters, digits and
|
|
underscores.
|
|
Variables may be given values by writing, for example,
|
|
.DS
|
|
user=fred\ box=m000\ acct=mh0000
|
|
.DE
|
|
which assigns values to the variables
|
|
\fBuser, box\fP and \fBacct.\fP
|
|
A variable may be set to the null string
|
|
by saying, for example,
|
|
.DS
|
|
null=
|
|
.DE
|
|
The value of a variable is substituted
|
|
by preceding its name with \fB$\|\fP;
|
|
for example,
|
|
.DS
|
|
echo $user
|
|
.DE
|
|
will echo \fIfred.\fP
|
|
.LP
|
|
Variables may be used interactively
|
|
to provide abbreviations for frequently
|
|
used strings.
|
|
For example,
|
|
.DS
|
|
b=/usr/fred/bin
|
|
mv pgm $b
|
|
.DE
|
|
will move the file \fIpgm\fP
|
|
from the current directory to the directory \fB/usr/fred/bin\|.\fR
|
|
A more general notation is available for parameter
|
|
(or variable)
|
|
substitution, as in,
|
|
.DS
|
|
echo ${user}
|
|
.DE
|
|
which is equivalent to
|
|
.DS
|
|
echo $user
|
|
.DE
|
|
and is used when the parameter name is
|
|
followed by a letter or digit.
|
|
For example,
|
|
.DS
|
|
tmp=/tmp/ps
|
|
ps a >${tmp}a
|
|
.DE
|
|
will direct the output of \fIps\fR
|
|
to the file \fB/tmp/psa,\fR
|
|
whereas,
|
|
.DS
|
|
ps a >$tmpa
|
|
.DE
|
|
would cause the value of the variable \fBtmpa\fP
|
|
to be substituted.
|
|
.LP
|
|
Except for \fB$?\fP the following
|
|
are set initially by the shell.
|
|
\fB$?\fP is set after executing each command.
|
|
.RS
|
|
.IP \fB$?\fP 8
|
|
The exit status (return code)
|
|
of the last command executed
|
|
as a decimal string.
|
|
Most commands return a zero exit status
|
|
if they complete successfully,
|
|
otherwise a non-zero exit status is returned.
|
|
Testing the value of return codes is dealt with
|
|
later under \fBif\fP and \fBwhile\fP commands.
|
|
.IP \fB$#\fP 8
|
|
The number of positional parameters
|
|
(in decimal).
|
|
Used, for example, in the \fIappend\fP command
|
|
to check the number of parameters.
|
|
.IP \fB$$\fP 8
|
|
The process number of this shell (in decimal).
|
|
Since process numbers are unique among
|
|
all existing processes, this string is
|
|
frequently used to generate
|
|
unique
|
|
temporary file names.
|
|
For example,
|
|
.DS
|
|
ps a >/tmp/ps$$
|
|
\*(ZZ
|
|
rm /tmp/ps$$
|
|
.DE
|
|
.IP \fB$\|!\fP 8
|
|
The process number of the last process
|
|
run in the background (in decimal).
|
|
.IP \fB$\(mi\fP 8
|
|
The current shell flags, such as
|
|
\fB\(mix\fR and \fB\(miv\|.\fR
|
|
.RE
|
|
.LP
|
|
Some variables have a special meaning to the
|
|
shell and should be avoided for general
|
|
use.
|
|
.RS
|
|
.IP \fB$\s-1MAIL\s0\fP 8
|
|
When used interactively
|
|
the shell looks at the file
|
|
specified by this variable
|
|
before it issues a prompt.
|
|
If the specified file has been modified
|
|
since it
|
|
was last looked at the shell
|
|
prints the message
|
|
\fIyou have mail\fP before prompting
|
|
for the next command.
|
|
This variable is typically set
|
|
in the file \fB.profile,\fP
|
|
in the user's login directory.
|
|
For example,
|
|
.DS
|
|
\s-1MAIL\s0=/usr/spool/mail/fred
|
|
.DE
|
|
.IP \fB$\s-1HOME\s0\fP 8
|
|
The default argument
|
|
for the \fIcd\fP command.
|
|
The current directory is used to resolve
|
|
file name references that do not begin with
|
|
a \fB/\|,\fR
|
|
and is changed using the \fIcd\fP command.
|
|
For example,
|
|
.DS
|
|
cd /usr/fred/bin
|
|
.DE
|
|
makes the current directory \fB/usr/fred/bin\|.\fR
|
|
.DS
|
|
cat wn
|
|
.DE
|
|
will print on the terminal the file \fIwn\fP
|
|
in this directory.
|
|
The command
|
|
\fIcd\fP with no argument
|
|
is equivalent to
|
|
.DS
|
|
cd $\s-1HOME\s0
|
|
.DE
|
|
This variable is also typically set in the
|
|
the user's login profile.
|
|
.IP \fB$\s-1PWD\s0\fP 8
|
|
The current working directory. Set by the \fIcd\fB command.
|
|
.IP \fB$\s-1PATH\s0\fP 8
|
|
A list of directories that contain commands (the \fIsearch path\fR\|).
|
|
Each time a command is executed by the shell
|
|
a list of directories is searched
|
|
for an executable file.
|
|
.ne 5
|
|
If \fB$\s-1PATH\s0\fP is not set
|
|
then the current directory,
|
|
\fB/bin\fP, and \fB/usr/bin\fP are searched by default.
|
|
.ne 5
|
|
Otherwise \fB$\s-1PATH\s0\fP consists of directory
|
|
names separated by \fB:\|.\fP
|
|
For example,
|
|
.DS
|
|
\s-1PATH\s0=\fB:\fP/usr/fred/bin\fB:\fP/bin\fB:\fP/usr/bin
|
|
.DE
|
|
specifies that the current directory
|
|
(the null string before the first \fB:\fP\|),
|
|
\fB/usr/fred/bin, /bin \fRand\fP /usr/bin\fR
|
|
are to be searched in that order.
|
|
In this way individual users
|
|
can have their own `private' commands
|
|
that are accessible independently
|
|
of the current directory.
|
|
If the command name contains a \fB/\fR then this directory search
|
|
is not used; a single attempt
|
|
is made to execute the command.
|
|
.IP \fB$\s-1PS1\s0\fP 8
|
|
The primary shell prompt string, by default, `\fB$\ \fR'.
|
|
.IP \fB$\s-1PS2\s0\fP 8
|
|
The shell prompt when further input is needed,
|
|
by default, `\fB>\ \fR'.
|
|
.IP \fB$\s-1IFS\s0\fP 8
|
|
The set of characters used by \fIblank
|
|
interpretation\fR (see section 3.5).
|
|
.IP \fB$\s-1ENV\s0\fP 8
|
|
The shell reads and executes the commands in the file
|
|
specified by this variable when it is initially started.
|
|
Unlike the \fB.profile\fP file, these commands are executed by all
|
|
shells, not just the one started at login.
|
|
(Most versions of the shell specify a filename that is used if
|
|
\s-1ENV\s0 is not explicitly set. See the manual page for your shell.)
|
|
.RE
|
|
.SH
|
|
2.5\ The\ test\ command
|
|
.LP
|
|
The \fItest\fP command, although not part of the shell,
|
|
is intended for use by shell programs.
|
|
For example,
|
|
.DS
|
|
test \(mif file
|
|
.DE
|
|
returns zero exit status if \fIfile\fP
|
|
exists and non-zero exit status otherwise.
|
|
In general \fItest\fP evaluates a predicate
|
|
and returns the result as its exit status.
|
|
Some of the more frequently used \fItest\fP
|
|
arguments are given here, see \fItest\fP(1)
|
|
for a complete specification.
|
|
.DS
|
|
test s true if the argument \fIs\fP is not the null string
|
|
test \(mif file true if \fIfile\fP exists
|
|
test \(mir file true if \fIfile\fP is readable
|
|
test \(miw file true if \fIfile\fP is writable
|
|
test \(mid file true if \fIfile\fP is a directory
|
|
.DE
|
|
The \fItest\fP command is known as `\fB[\fP' and may be invoked as
|
|
such.
|
|
For aesthetic reasons, the command ignores a close bracket `\fB]\fP' given
|
|
at the end of a command so
|
|
.DS
|
|
[ -f filename ]
|
|
.DE
|
|
and
|
|
.DS
|
|
test -f filename
|
|
.DE
|
|
are completely equivalent.
|
|
Typically, the bracket notation is used when \fItest\fP is invoked inside
|
|
shell control constructs.
|
|
.SH
|
|
2.6\ Control\ flow\ -\ while
|
|
.LP
|
|
The actions of
|
|
the \fBfor\fP loop and the \fBcase\fP
|
|
branch are determined by data available to the shell.
|
|
A \fBwhile\fP or \fBuntil\fP loop
|
|
and an \fBif then else\fP branch
|
|
are also provided whose
|
|
actions are determined by the exit status
|
|
returned by commands.
|
|
A \fBwhile\fP loop has the general form
|
|
.DS
|
|
\fBwhile\fP \fIcommand-list\*1\fP
|
|
\fBdo\fP \fIcommand-list\*2\fP
|
|
\fBdone\fP
|
|
.DE
|
|
.LP
|
|
The value tested by the \fBwhile\fP command
|
|
is the exit status of the last simple command
|
|
following \fBwhile.\fP
|
|
Each time round the loop
|
|
\fIcommand-list\*1\fP is executed;
|
|
if a zero exit status is returned then
|
|
\fIcommand-list\*2\fP
|
|
is executed;
|
|
otherwise, the loop terminates.
|
|
For example,
|
|
.DS
|
|
while [ $1 ]
|
|
do \*(ZZ
|
|
\*(DOshift
|
|
done
|
|
.DE
|
|
is equivalent to
|
|
.DS
|
|
for i
|
|
do \*(ZZ
|
|
done
|
|
.DE
|
|
\fIshift\fP is a shell command that
|
|
renames the positional parameters
|
|
\fB$2, $3, \*(ZZ\fR as \fB$1, $2, \*(ZZ\fR
|
|
and loses \fB$1\|.\fP
|
|
.LP
|
|
Another kind of use for the \fBwhile/until\fP
|
|
loop is to wait until some
|
|
external event occurs and then run
|
|
some commands.
|
|
In an \fBuntil\fP loop
|
|
the termination condition is reversed.
|
|
For example,
|
|
.DS
|
|
until [ \(mif file ]
|
|
do sleep 300; done
|
|
\fIcommands\fP
|
|
.DE
|
|
will loop until \fIfile\fP exists.
|
|
Each time round the loop it waits for
|
|
5 minutes before trying again.
|
|
(Presumably another process
|
|
will eventually create the file.)
|
|
.LP
|
|
The most recent enclosing loop may be exited with the \fBbreak\fP
|
|
command, or the rest of the body skipped and the next iteration begun
|
|
with the \fBcontinue\fP command.
|
|
.LP
|
|
The commands \fItrue\fP(1) and \fIfalse\fP(1) return 0 and non-zero
|
|
exit statuses respectively. They are sometimes of use in control flow,
|
|
e.g.:
|
|
.DS
|
|
while true
|
|
do date; sleep 5
|
|
done
|
|
.DE
|
|
is an infinite loop that prints the date and time every five seconds.
|
|
.SH
|
|
2.7\ Control\ flow\ -\ if
|
|
.LP
|
|
Also available is a
|
|
general conditional branch
|
|
of the form,
|
|
.DS
|
|
\fBif\fP \fIcommand-list
|
|
\fBthen \fIcommand-list
|
|
\fBelse \fIcommand-list
|
|
\fBfi\fR
|
|
.DE
|
|
that tests the value returned by the last simple command
|
|
following \fBif.\fP
|
|
.LP
|
|
The \fBif\fP command may be used
|
|
in conjunction with the \fItest\fP command
|
|
to test for the existence of a file as in
|
|
.DS
|
|
if [ \(mif file ]
|
|
then \fIprocess file\fP
|
|
else \fIdo something else\fP
|
|
fi
|
|
.DE
|
|
.LP
|
|
An example of the use of \fBif, case\fP
|
|
and \fBfor\fP constructions is given in
|
|
section 2.10\|.
|
|
.LP
|
|
A multiple test \fBif\fP command
|
|
of the form
|
|
.DS
|
|
if \*(ZZ
|
|
then \*(ZZ
|
|
else if \*(ZZ
|
|
then \*(ZZ
|
|
else if \*(ZZ
|
|
\*(ZZ
|
|
fi
|
|
fi
|
|
fi
|
|
.DE
|
|
may be written using an extension of the \fBif\fP
|
|
notation as,
|
|
.DS
|
|
if \*(ZZ
|
|
then \*(ZZ
|
|
elif \*(ZZ
|
|
then \*(ZZ
|
|
elif \*(ZZ
|
|
\*(ZZ
|
|
fi
|
|
.DE
|
|
.LP
|
|
The following example is an implementation of the \fItouch\fP command
|
|
which changes the `last modified' time for a list
|
|
of files.
|
|
The command may be used in conjunction
|
|
with \fImake\fP(1) to force recompilation of a list
|
|
of files.
|
|
.DS
|
|
#!/bin/sh
|
|
|
|
flag=
|
|
for i
|
|
do case $i in
|
|
\*(DC\(mic) flag=N ;;
|
|
\*(DC\*(ST) if [ \(mif $i ]
|
|
\*(DC then cp $i junk$$; mv junk$$ $i
|
|
\*(DC elif [ $flag ]
|
|
\*(DC then echo file \\'$i\\' does not exist
|
|
\*(DC else >$i
|
|
\*(DC fi
|
|
\*(DO esac
|
|
done
|
|
.DE
|
|
The \fB\(mic\fP flag is used in this command to
|
|
force subsequent files to be created if they do not already exist.
|
|
Otherwise, if the file does not exist, an error message is printed.
|
|
The shell variable \fIflag\fP
|
|
is set to some non-null string if the \fB\(mic\fP
|
|
argument is encountered.
|
|
The commands
|
|
.DS
|
|
cp \*(ZZ; mv \*(ZZ
|
|
.DE
|
|
copy the file and then overwrite it with the copy,
|
|
thus causing the last modified date to be updated.
|
|
.LP
|
|
The sequence
|
|
.DS
|
|
if command1
|
|
then command2
|
|
fi
|
|
.DE
|
|
may be written
|
|
.DS
|
|
command1 && command2
|
|
.DE
|
|
Conversely,
|
|
.DS
|
|
command1 \*(VT\*(VT command2
|
|
.DE
|
|
executes \fIcommand2\fP only if \fIcommand1\fP
|
|
fails.
|
|
In each case the value returned
|
|
is that of the last simple command executed.
|
|
.LP
|
|
Placing a `\fB!\fP' in front of a pipeline inverts its exit
|
|
status, almost in the manner of the C operator of the same name.
|
|
Thus:
|
|
.DS
|
|
if ! [ -d $1 ]
|
|
then
|
|
echo $1 is not a directory
|
|
fi
|
|
.DE
|
|
will print a message only if $1 is not a directory.
|
|
.SH
|
|
2.8\ Command\ grouping
|
|
.LP
|
|
Commands may be grouped in two ways,
|
|
.DS
|
|
\fB{\fI command-list\fB ; }\fR
|
|
.DE
|
|
and
|
|
.DS
|
|
\fB(\fI command-list\fB )\fR
|
|
.DE
|
|
.LP
|
|
In the first \fIcommand-list\fP is simply executed.
|
|
The second form executes \fIcommand-list\fP
|
|
as a separate process.
|
|
For example,
|
|
.DS
|
|
(cd x; rm junk )
|
|
.DE
|
|
executes \fIrm junk\fP in the directory
|
|
\fBx\fP without changing the current
|
|
directory of the invoking shell.
|
|
.LP
|
|
The commands
|
|
.DS
|
|
cd x; rm junk
|
|
.DE
|
|
have the same effect but leave the invoking
|
|
shell in the directory \fBx.\fP
|
|
.SH
|
|
2.9\ Shell\ Functions
|
|
.LP
|
|
A function may be defined by the syntax
|
|
.DS
|
|
\fIfuncname\fP() \fB{\fI command-list\fB ; }\fR
|
|
.DE
|
|
Functions are invoked within a script as though they were separate
|
|
commands of the same name.
|
|
While they are executed, the
|
|
positional parameters \fB$1, $2, \*(ZZ\fR are temporarily set to the
|
|
arguments passed to the function. For example:
|
|
.DS
|
|
count() {
|
|
echo $2 : $#
|
|
}
|
|
|
|
count a b c
|
|
.DE
|
|
would print `b : 3'.
|
|
.SH
|
|
2.10\ Debugging\ shell\ scripts
|
|
.LP
|
|
The shell provides two tracing mechanisms
|
|
to help when debugging shell scripts.
|
|
The first is invoked within the script
|
|
as
|
|
.DS
|
|
set \(miv
|
|
.DE
|
|
(\fBv\fP for verbose) and causes lines of the
|
|
script to be printed as they are read.
|
|
It is useful to help isolate syntax errors.
|
|
It may be invoked without modifying the script
|
|
by saying
|
|
.DS
|
|
sh \(miv \fIscript\fP \*(ZZ
|
|
.DE
|
|
where \fIscript\fP is the name of the shell script.
|
|
This flag may be used in conjunction
|
|
with the \fB\(min\fP flag which prevents
|
|
execution of subsequent commands.
|
|
(Note that saying \fIset \(min\fP at a terminal
|
|
will render the terminal useless
|
|
until an end-of-file is typed.)
|
|
.LP
|
|
The command
|
|
.DS
|
|
set \(mix
|
|
.DE
|
|
will produce an execution
|
|
trace.
|
|
Following parameter substitution
|
|
each command is printed as it is executed.
|
|
(Try these at the terminal to see
|
|
what effect they have.)
|
|
Both flags may be turned off by saying
|
|
.DS
|
|
set \(mi
|
|
.DE
|
|
and the current setting of the shell flags is available as \fB$\(mi\|\fR.
|
|
.SH
|
|
2.11\ The\ man\ command
|
|
.LP
|
|
The following is a simple implementation of the \fIman\fP command,
|
|
which is used to display sections of the UNIX manual on your terminal.
|
|
It is called, for example, as
|
|
.DS
|
|
man sh
|
|
man \(mit ed
|
|
man 2 fork
|
|
.DE
|
|
In the first the manual section for \fIsh\fP
|
|
is displayed..
|
|
Since no section is specified, section 1 is used.
|
|
The second example will typeset (\fB\(mit\fP option)
|
|
the manual section for \fIed.\fP
|
|
The last prints the \fIfork\fP manual page
|
|
from section 2, which covers system calls.
|
|
.sp 2
|
|
.DS
|
|
#!/bin/sh
|
|
|
|
cd /usr/share/man
|
|
|
|
# "#" is the comment character
|
|
# default is nroff ($N), section 1 ($s)
|
|
N=n\ s=1
|
|
|
|
for i
|
|
do case $i in
|
|
.sp .5
|
|
\*(DC[1\(mi9]\*(ST) s=$i ;;
|
|
.sp .5
|
|
\*(DC\(mit) N=t ;;
|
|
.sp .5
|
|
\*(DC\(min) N=n ;;
|
|
.sp .5
|
|
\*(DC\(mi\*(ST) echo unknown flag \\'$i\\' ;;
|
|
.sp .5
|
|
\*(DC\*(ST) if [ \(mif man$s/$i.$s ]
|
|
\*(DC then
|
|
\*(DC ${N}roff \(miman man$s/$i.$s
|
|
\*(DC else # look through all manual sections
|
|
\*(DC found=no
|
|
\*(DC for j in 1 2 3 4 5 6 7 8 9
|
|
\*(DC do
|
|
\*(DC \*(DOif [ \(mif man$j/$i.$j ]
|
|
\*(DC \*(DOthen
|
|
\*(DC \*(DO\*(THman $j $i
|
|
\*(DC \*(DO\*(THfound=yes
|
|
\*(DC \*(DO\*(THbreak
|
|
\*(DC \*(DOfi
|
|
\*(DC done
|
|
\*(DC case $found in
|
|
\*(DC \*(Cano) echo \\'$i: manual page not found\\'
|
|
\*(DC esac
|
|
\*(DC fi
|
|
\*(DOesac
|
|
done
|
|
.DE
|
|
.ce
|
|
.ft B
|
|
Figure 1. A version of the man command
|
|
.ft R
|