.\" $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