home *** CD-ROM | disk | FTP | other *** search
- .\" @(#)t2 6.1 (Berkeley) 5/22/86
- .\"
- .SH
- 2.0\ Shell\ procedures
- .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 \fIcommand procedure\fP
- or \fIshell procedure.\fP
- Arguments may be supplied with the call
- and are referred to in \fIfile\fP
- using the positional parameters
- \fB$1, $2, \*(ZZ\|.\fR
- 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 procedures and programs
- to be used interchangeably.
- In either case a new process is created to
- run the command.
- .LP
- As well as providing names for the positional
- parameters,
- the number of positional parameters in the call
- 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.
- .SH
- 2.1\ Control\ flow\ -\ for
- .LP
- A frequent use of shell procedures is to loop
- through the arguments (\fB$1, $2, \*(ZZ\fR)
- executing commands once for each argument.
- An example of such a procedure is
- \fItel\fP that searches the file
- \fB/usr/lib/telnos\fR
- that contains lines of the form
- .DS
- \*(ZZ
- fred mh0123
- bert mh0789
- \*(ZZ
- .DE
- The text of \fItel\fP is
- .DS
- for i
- do grep $i /usr/lib/telnos; done
- .DE
- The command
- .DS
- tel fred
- .DE
- prints those lines in \fB/usr/lib/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 procedure \fItel\fP
- in section 2.1 uses the file \fB/usr/lib/telnos\fR
- to supply the data
- for \fIgrep.\fP
- An alternative is to include this
- data
- within the shell procedure 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 procedure
- 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\\\\#
- \*(ZZ
- #
- .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
- .LP
- 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-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.4).
- .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
- .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 test $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 test \(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.)
- .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 test \(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 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
- flag=
- for i
- do case $i in
- \*(DC\(mic) flag=N ;;
- \*(DC\*(ST) if test \(mif $i
- \*(DC then ln $i junk$$; rm junk$$
- \*(DC elif test $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
- ln \*(ZZ; rm \*(ZZ
- .DE
- make a link to the file and then remove it
- 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.
- .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\ Debugging\ shell\ procedures
- .LP
- The shell provides two tracing mechanisms
- to help when debugging shell procedures.
- The first is invoked within the procedure
- as
- .DS
- set \(miv
- .DE
- (\fBv\fP for verbose) and causes lines of the
- procedure to be printed as they are read.
- It is useful to help isolate syntax errors.
- It may be invoked without modifying the procedure
- by saying
- .DS
- sh \(miv proc \*(ZZ
- .DE
- where \fIproc\fP is the name of the shell procedure.
- 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.10\ The\ man\ command
- .LP
- The following is the \fIman\fP command
- which is used to diplay 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
- cd /usr/man
-
- : \'colon is the comment command\'
- : \'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 test \(mif man$s/$i.$s
- \*(DC then ${N}roff man0/${N}aa 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 if test \(mif man$j/$i.$j
- \*(DC \*(DOthen man $j $i
- \*(DC \*(DO\*(THfound=yes
- \*(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
-