home *** CD-ROM | disk | FTP | other *** search
- .\" Copyright (c) 1980 Regents of the University of California.
- .\" All rights reserved. The Berkeley software License Agreement
- .\" specifies the terms and conditions for redistribution.
- .\"
- .\" @(#)csh.3 6.1 (Berkeley) 5/23/86
- .\"
- .nr H1 2
- .NH
- Shell control structures and command scripts
- .NH 2
- Introduction
- .PP
- It is possible to place commands in files and to cause shells to be
- invoked to read and execute commands from these files,
- which are called
- .I "shell scripts."
- We here detail those features of the shell useful to the writers of such
- scripts.
- .NH 2
- Make
- .PP
- It is important to first note what shell scripts are
- .I not
- useful for.
- There is a program called
- .I make
- which is very useful for maintaining a group of related files
- or performing sets of operations on related files.
- For instance a large program consisting of one or more files
- can have its dependencies described in a
- .I makefile
- which contains definitions of the commands used to create these
- different files when changes occur.
- Definitions of the means for printing listings, cleaning up the directory
- in which the files reside, and installing the resultant programs
- are easily, and most appropriately placed in this
- .I makefile.
- This format is superior and preferable to maintaining a group of shell
- procedures to maintain these files.
- .PP
- Similarly when working on a document a
- .I makefile
- may be created which defines how different versions of the document
- are to be created and which options of
- .I nroff
- or
- .I troff
- are appropriate.
- .NH 2
- Invocation and the argv variable
- .PP
- A
- .I csh
- command script may be interpreted by saying
- .DS
- % csh script ...
- .DE
- where
- .I script
- is the name of the file containing a group of
- .I csh
- commands and
- `\&...' is replaced by a sequence of arguments.
- The shell places these arguments in the variable
- .I argv
- and then begins to read commands from the script.
- These parameters are then available through the same mechanisms
- which are used to reference any other shell variables.
- .PP
- If you make the file
- `script'
- executable by doing
- .DS
- chmod 755 script
- .DE
- and place a shell comment at the beginning of the shell script
- (i.e. begin the file with a `#' character)
- then a `/bin/csh' will automatically be invoked to execute `script' when
- you type
- .DS
- script
- .DE
- If the file does not begin with a `#' then the standard shell
- `/bin/sh' will be used to execute it.
- This allows you to convert your older shell scripts to use
- .I csh
- at your convenience.
- .NH 2
- Variable substitution
- .PP
- After each input line is broken into words and history substitutions
- are done on it, the input line is parsed into distinct commands.
- Before each command is executed a mechanism know as
- .I "variable substitution"
- is done on these words.
- Keyed by the character `$' this substitution replaces the names
- of variables by their values.
- Thus
- .DS
- echo $argv
- .DE
- when placed in a command script would cause the current value of the
- variable
- .I argv
- to be echoed to the output of the shell script.
- It is an error for
- .I argv
- to be unset at this point.
- .PP
- A number of notations are provided for accessing components and attributes
- of variables.
- The notation
- .DS
- $?name
- .DE
- expands to `1' if name is
- .I set
- or to `0'
- if name is not
- .I set.
- It is the fundamental mechanism used for checking whether particular
- variables have been assigned values.
- All other forms of reference to undefined variables cause errors.
- .PP
- The notation
- .DS
- $#name
- .DE
- expands to the number of elements in the variable
- .I name.
- Thus
- .DS
- % set argv=(a b c)
- % echo $?argv
- 1
- % echo $#argv
- 3
- % unset argv
- % echo $?argv
- 0
- % echo $argv
- Undefined variable: argv.
- %
- .DE
- .PP
- It is also possible to access the components of a variable
- which has several values.
- Thus
- .DS
- $argv[1]
- .DE
- gives the first component of
- .I argv
- or in the example above `a'.
- Similarly
- .DS
- $argv[$#argv]
- .DE
- would give `c',
- and
- .DS
- $argv[1\-2]
- .DE
- would give `a b'. Other notations useful in shell scripts are
- .DS
- $\fIn\fR
- .DE
- where
- .I n
- is an integer as a shorthand for
- .DS
- $argv[\fIn\fR\|]
- .DE
- the
- .I n\|th
- parameter and
- .DS
- $*
- .DE
- which is a shorthand for
- .DS
- $argv
- .DE
- The form
- .DS
- $$
- .DE
- expands to the process number of the current shell.
- Since this process number is unique in the system it can
- be used in generation of unique temporary file names.
- The form
- .DS
- $<
- .DE
- is quite special and is replaced by the next line of input read from
- the shell's standard input (not the script it is reading). This is
- useful for writing shell scripts that are interactive, reading
- commands from the terminal, or even writing a shell script that
- acts as a filter, reading lines from its input file. Thus the sequence
- .DS
- echo 'yes or no?\ec'
- set a=($<)
- .DE
- would write out the prompt `yes or no?' without a newline and then
- read the answer into the variable `a'. In this case `$#a' would be
- `0' if either a blank line or end-of-file (^D) was typed.
- .PP
- One minor difference between `$\fIn\fR\|' and `$argv[\fIn\fR\|]'
- should be noted here.
- The form
- `$argv[\fIn\fR\|]'
- will yield an error if
- .I n
- is not in the range
- `1\-$#argv'
- while `$n'
- will never yield an out of range subscript error.
- This is for compatibility with the way older shells handled parameters.
- .PP
- Another important point is that it is never an error to give a subrange
- of the form `n\-'; if there are less than
- .I n
- components of the given variable then no words are substituted.
- A range of the form `m\-n' likewise returns an empty vector without giving
- an error when \fIm\fR exceeds the number of elements of the given variable,
- provided the subscript \fIn\fR is in range.
- .NH 2
- Expressions
- .PP
- In order for interesting shell scripts to be constructed it
- must be possible to evaluate expressions in the shell based on the
- values of variables.
- In fact, all the arithmetic operations of the language C are available
- in the shell
- with the same precedence that they have in C.
- In particular, the operations `==' and `!=' compare strings
- and the operators `&&' and `|\|\||' implement the boolean and/or operations.
- The special operators `=~' and `!~' are similar to `==' and `!=' except
- that the string on the right side can have pattern matching characters
- (like *, ? or []) and the test is whether the string on the left matches
- the pattern on the right.
- .PP
- The shell also allows file enquiries of the form
- .DS
- \-? filename
- .DE
- where `?' is replace by a number of single characters.
- For instance the expression primitive
- .DS
- \-e filename
- .DE
- tell whether the file
- `filename'
- exists.
- Other primitives test for read, write and execute access to the file,
- whether it is a directory, or has non-zero length.
- .PP
- It is possible to test whether a command terminates normally,
- by a primitive of the
- form `{ command }' which returns true, i.e. `1' if the command
- succeeds exiting normally with exit status 0, or `0' if the command
- terminates abnormally or with exit status non-zero.
- If more detailed information about the execution status of a command
- is required, it can be executed and the variable `$status' examined
- in the next command.
- Since `$status' is set by every command, it is very transient.
- It can be saved if it is inconvenient to use it only in the single
- immediately following command.
- .PP
- For a full list of expression components available see the manual
- section for the shell.
- .NH 2
- Sample shell script
- .PP
- A sample shell script which makes use of the expression mechanism
- of the shell and some of its control structure follows:
- .DS
- % cat copyc
- #
- # Copyc copies those C programs in the specified list
- # to the directory ~/backup if they differ from the files
- # already in ~/backup
- #
- set noglob
- foreach i ($argv)
-
- if ($i !~ *.c) continue # not a .c file so do nothing
-
- if (! \-r ~/backup/$i:t) then
- echo $i:t not in backup... not cp\e\'ed
- continue
- endif
-
- cmp \-s $i ~/backup/$i:t # to set $status
-
- if ($status != 0) then
- echo new backup of $i
- cp $i ~/backup/$i:t
- endif
- end
- .DE
- .PP
- This script makes use of the
- .I foreach
- command, which causes the shell to execute the commands between the
- .I foreach
- and the matching
- .I end
- for each of the values given between `(' and `)' with the named
- variable, in this case `i' set to successive values in the list.
- Within this loop we may use the command
- .I break
- to stop executing the loop
- and
- .I continue
- to prematurely terminate one iteration
- and begin the next.
- After the
- .I foreach
- loop the iteration variable
- (\fIi\fR in this case)
- has the value at the last iteration.
- .PP
- We set the variable
- .I noglob
- here to prevent filename expansion of the members of
- .I argv.
- This is a good idea, in general, if the arguments to a shell script
- are filenames which have already been expanded or if the arguments
- may contain filename expansion metacharacters.
- It is also possible to quote each use of a `$' variable expansion,
- but this is harder and less reliable.
- .PP
- The other control construct used here is a statement of the form
- .DS
- \fBif\fR ( expression ) \fBthen\fR
- command
- ...
- \fBendif\fR
- .DE
- The placement of the keywords here is
- .B not
- flexible due to the current implementation of the shell.\(dg
- .FS
- \(dgThe following two formats are not currently acceptable to the shell:
- .sp
- .in +5
- .nf
- \fBif\fR ( expression ) # \fBWon't work!\fR
- \fBthen\fR
- command
- ...
- \fBendif\fR
- .fi
- .in -5
- .sp
- and
- .sp
- .in +5
- .nf
- \fBif\fR ( expression ) \fBthen\fR command \fBendif\fR # \fBWon't work\fR
- .in -5
- .fi
- .FE
- .PP
- The shell does have another form of the if statement of the form
- .DS
- \fBif\fR ( expression ) \fBcommand\fR
- .DE
- which can be written
- .DS
- \fBif\fR ( expression ) \e
- command
- .DE
- Here we have escaped the newline for the sake of appearance.
- The command must not involve `\||\|', `&' or `;'
- and must not be another control command.
- The second form requires the final `\e' to
- .B immediately
- precede the end-of-line.
- .PP
- The more general
- .I if
- statements above also admit a sequence of
- .I else\-if
- pairs followed by a single
- .I else
- and an
- .I endif,
- e.g.:
- .DS
- \fBif\fR ( expression ) \fBthen\fR
- commands
- \fBelse\fR \fBif\fR (expression ) \fBthen\fR
- commands
- \&...
-
- \fBelse\fR
- commands
- \fBendif\fR
- .DE
- .PP
- Another important mechanism used in shell scripts is the `:' modifier.
- We can use the modifier `:r' here to extract a root of a filename or
- `:e' to extract the
- .I extension.
- Thus if the variable
- .I i
- has the value
- `/mnt/foo.bar'
- then
- .sp
- .in +5
- .nf
- % echo $i $i:r $i:e
- /mnt/foo.bar /mnt/foo bar
- %
- .sp
- .in -5
- .fi
- shows how the `:r' modifier strips off the trailing `.bar' and the
- the `:e' modifier leaves only the `bar'.
- Other modifiers will take off the last component of a pathname leaving
- the head `:h' or all but the last component of a pathname leaving the
- tail `:t'.
- These modifiers are fully described in the
- .I csh
- manual pages in the User's Reference Manual.
- It is also possible to use the
- .I "command substitution"
- mechanism described in the next major section to perform modifications
- on strings to then reenter the shell's environment.
- Since each usage of this mechanism involves the creation of a new process,
- it is much more expensive to use than the `:' modification mechanism.\(dd
- .FS
- \(dd It is also important to note that
- the current implementation of the shell limits the number of `:' modifiers
- on a `$' substitution to 1.
- Thus
- .sp
- .nf
- .in +5
- % echo $i $i:h:t
- /a/b/c /a/b:t
- %
- .in -5
- .fi
- .sp
- does not do what one would expect.
- .FE
- Finally, we note that the character `#' lexically introduces a shell
- comment in shell scripts (but not from the terminal).
- All subsequent characters on the input line after a `#' are discarded
- by the shell.
- This character can be quoted using `\'' or `\e' to place it in
- an argument word.
- .NH 2
- Other control structures
- .PP
- The shell also has control structures
- .I while
- and
- .I switch
- similar to those of C.
- These take the forms
- .DS
- \fBwhile\fR ( expression )
- commands
- \fBend\fR
- .DE
- and
- .DS
- \fBswitch\fR ( word )
-
- \fBcase\fR str1:
- commands
- \fBbreaksw\fR
-
- \& ...
-
- \fBcase\fR strn:
- commands
- \fBbreaksw\fR
-
- \fBdefault:\fR
- commands
- \fBbreaksw\fR
-
- \fBendsw\fR
- .DE
- For details see the manual section for
- .I csh.
- C programmers should note that we use
- .I breaksw
- to exit from a
- .I switch
- while
- .I break
- exits a
- .I while
- or
- .I foreach
- loop.
- A common mistake to make in
- .I csh
- scripts is to use
- .I break
- rather than
- .I breaksw
- in switches.
- .PP
- Finally,
- .I csh
- allows a
- .I goto
- statement, with labels looking like they do in C, i.e.:
- .DS
- loop:
- commands
- \fBgoto\fR loop
- .DE
- .NH 2
- Supplying input to commands
- .PP
- Commands run from shell scripts receive by default the standard
- input of the shell which is running the script.
- This is different from previous shells running
- under \s-2UNIX\s0. It allows shell scripts to fully participate
- in pipelines, but mandates extra notation for commands which are to take
- inline data.
- .PP
- Thus we need a metanotation for supplying inline data to commands in
- shell scripts.
- As an example, consider this script which runs the editor to
- delete leading blanks from the lines in each argument file:
- .DS
- % cat deblank
- # deblank \-\- remove leading blanks
- foreach i ($argv)
- ed \- $i << \'EOF\'
- 1,$s/^[ ]*//
- w
- q
- \&\'EOF\'
- end
- %
- .DE
- The notation `<< \'EOF\''
- means that the standard input for the
- .I ed
- command is to come from the text in the shell script file
- up to the next line consisting of exactly `\'EOF\''.
- The fact that the `EOF' is enclosed in `\'' characters, i.e. quoted,
- causes the shell to not perform variable substitution on the
- intervening lines.
- In general, if any part of the word following the `<<' which the
- shell uses to terminate the text to be given to the command is quoted
- then these substitutions will not be performed.
- In this case since we used the form `1,$' in our editor script
- we needed to insure that this `$' was not variable substituted.
- We could also have insured this by preceding the `$' here with a `\e',
- i.e.:
- .DS
- 1,\e$s/^[ ]*//
- .DE
- but quoting the `EOF' terminator is a more reliable way of achieving the
- same thing.
- .NH 2
- Catching interrupts
- .PP
- If our shell script creates temporary files, we may wish to catch
- interruptions of the shell script so that we can clean up
- these files.
- We can then do
- .DS
- onintr label
- .DE
- where
- .I label
- is a label in our program.
- If an interrupt is received the shell will do a
- `goto label'
- and we can remove the temporary files and then do an
- .I exit
- command (which is built in to the shell)
- to exit from the shell script.
- If we wish to exit with a non-zero status we can do
- .DS
- exit(1)
- .DE
- e.g. to exit with status `1'.
- .NH 2
- What else?
- .PP
- There are other features of the shell useful to writers of shell
- procedures.
- The
- .I verbose
- and
- .I echo
- options and the related
- .I \-v
- and
- .I \-x
- command line options can be used to help trace the actions of the shell.
- The
- .I \-n
- option causes the shell only to read commands and not to execute
- them and may sometimes be of use.
- .PP
- One other thing to note is that
- .I csh
- will not execute shell scripts which do not begin with the
- character `#', that is shell scripts that do not begin with a comment.
- Similarly, the `/bin/sh' on your system may well defer to `csh'
- to interpret shell scripts which begin with `#'.
- This allows shell scripts for both shells to live in harmony.
- .PP
- There is also another quotation mechanism using `"' which allows
- only some of the expansion mechanisms we have so far discussed to occur
- on the quoted string and serves to make this string into a single word
- as `\'' does.
- .bp
-