This is an HTMLized version of Steve Bourne's original shell tutorial. I found the source at http://cm.bell-labs.com/7thEdMan/vol2/shell.bun and generated my own copy with troff, which I then edited into an HTML version.This 1978 classic (not sure about the exact date, corrections welcome) is not only historically interesting, it's still a good tutorial as long as you keep in mind that some minor details like the terminal interface have changed quite a bit (DEL is not usually the interrupt character anymore, etc). The same goes, by the way, for a lot of the old Unix documentation from Murray Hill, including the excellent book The Unix Programming Environment by Kernighan and Pike.
You will find numerous other copies of this on the web, all of them -- as far as I can tell -- badly malformatted; for example, the pipe character will be completely missing throughout the document!
The original version I found was "updated for 4.3BSD by Mark Seiden" but as I didn't have Seiden's troff source for the updated version, only an online copy with the usual formatting errors, and it had very marginal changes as compared to the older version at Bell (remark that this is not a csh tutorial; different mail spool directory; mentioned that section 2 of the manual covers system calls; BSD has some new signals), I decided to go with the original.
If you find errors in this or otherwise wish to comment, please don't hesitate to mail me.
/* era */
The shell is a command programming language that provides an interface to the UNIX operating system. Its features include control-flow primitives, parameter passing, variables and string substitution. Constructs such as while, if then else, case and for are available. Two-way communication is possible between the shell and commands. String-valued parameters, typically file names or flags, may be passed to a command. A return code is set by commands that may be used to determine control-flow, and the standard output from a command may be used as shell input.The shell can modify the environment in which commands run. Input and output can be redirected to files, and processes that communicate through `pipes' can be invoked. Commands are found by searching directories in the file system in a sequence that can be defined by the user. Commands can be read either from the terminal or from a file, which allows command procedures to be stored for later use.
S. R. Bourne
is a command that prints the names of users logged in. The commandwho
prints a list of files in the current directory. The argument -l tells ls to print status information, size and the creation date for each file.ls -l
calls the C compiler to compile the file pgm.c. The trailing & is an operator that instructs the shell not to wait for the command to finish. To help keep track of such a process the shell reports its process number following its creation. A list of currently active processes may be obtained using the ps command.cc pgm.c &
The notation >file is interpreted by the shell and is not passed as an argument to ls. If file does not exist then the shell creates it; otherwise the original contents of file are replaced with the output from ls. Output may be appended to a file using the notationls -l >file
In this case file is also created if it does not already exist.ls -l >>file
The standard input of a command may be taken from a file instead of the terminal by writing, for example,
The command wc reads its standard input (in this case redirected from file) and prints the number of characters, words and lines found. If only the number of lines is required thenwc <file
could be used.wc -l <file
Two commands connected in this way constitute a pipeline and the overall effect is the same asls -l | wc
except that no file is used. Instead the two processes are connected by a pipe (see pipe (2)) and are run in parallel.ls -l >file; wc <file
Pipes are unidirectional and synchronization is achieved by halting wc when there is nothing to read and halting ls when the pipe is full.
A filter is a command that reads its standard input, transforms it in some way, and prints the result as output. One such filter, grep, selects from its input those lines that contain some specified string. For example,
prints those lines, if any, of the output from ls that contain the string old. Another useful filter is sort. For example,ls | grep old
will print an alphabetically sorted list of logged in users.who | sort
A pipeline may consist of more than two commands, for example,
prints the number of file names in the current directory containing the string old.ls | grep old | wc -l
prints information relating to the file main.c.ls -l main.c
The shell provides a mechanism for generating a list of file names that match a pattern. For example,
generates, as arguments to ls, all file names in the current directory that end in .c. The character * is a pattern that will match any string including the null string. In general patterns are specified as follows.ls -l *.c
matches all names in the current directory beginning with one of the letters a through z.[a-z]*
matches all names in the directory /usr/fred/test that consist of a single character. If no file name is found that matches the pattern then the pattern is passed, unchanged, as an argument./usr/fred/test/?
This mechanism is useful both to save typing and to select names according to some pattern. It may also be used to find files. For example,
finds and prints the names of all core files in sub-directories of /usr/fred. (echo is a standard UNIX command that prints its arguments, separated by blanks.) This last feature can be expensive, requiring a scan of all sub-directories of /usr/fred.echo /usr/fred/*/core
There is one exception to the general rules given for patterns. The character `.' at the start of a file name must be explicitly matched.
will therefore echo all file names in the current directory not beginning with `.'.echo *
will echo all those file names that begin with `.'. This avoids inadvertent matching of the names `.' and `..' which mean `the current directory' and `the parent directory' respectively. (Notice that ls suppresses information for the files `.' and `..'.)echo .*
will echo a single ?, andecho \?
will echo a single \. To allow long strings to be continued over more than one line the sequence \newline is ignored.echo \\
\ is convenient for quoting single characters. When more than one character needs quoting the above mechanism is clumsy and error prone. A string of characters may be quoted by enclosing the string between single quotes. For example,
will echoecho xx'****'xx
The quoted string may not contain a single quote but may contain newlines, which are preserved. This quoting mechanism is the most simple and is recommended for casual use.xx****xx
A third quoting mechanism using double quotes is also available that prevents interpretation of some but not all metacharacters. Discussion of the details is deferred to section 3.4.
that sets the prompt to be the string yesdear. If a newline is typed and further input is needed then the shell will issue the prompt `> '. Sometimes this can be caused by mistyping a quote mark. If it is unexpected then an interrupt (DEL) will return the shell to read another command. This prompt may be changed by saying, for example,PS1=yesdear
PS2=more
calls the shell to read commands from file. Such a file is called a command procedure or shell procedure. Arguments may be supplied with the call and are referred to in file using the positional parameters $1, $2, .... For example, if the file wg containssh file [ args ... ]
thenwho | grep $1
is equivalent tosh wg fred
UNIX files have three independent attributes, read, write and execute. The UNIX command chmod (1) may be used to make a file executable. For example,who | grep fred
will ensure that the file wg has execute status. Following this, the commandchmod +x wg
is equivalent towg fred
This allows shell procedures and programs to be used interchangeably. In either case a new process is created to run the command.sh wg fred
As well as providing names for the positional parameters, the number of positional parameters in the call is available as $#. The name of the file being executed is available as $0.
A special shell parameter $* is used to substitute for all positional parameters except $0. A typical use of this is to provide some default arguments, as in,
which simply prepends some arguments to those already given.nroff -T450 -ms $*
The text of tel is... fred mh0123 bert mh0789 ...
The commandfor i do grep $i /usr/lib/telnos; done
prints those lines in /usr/lib/telnos that contain the string fred.tel fred
prints those lines containing fred followed by those for bert.tel fred bert
The for loop notation is recognized by the shell and has the general form
A command-list is a sequence of one or more simple commands separated or terminated by a newline or semicolon. Furthermore, reserved words like do and done are only recognized following a newline or semicolon. name is a shell variable that is set to the words w1 w2 ... in turn each time the command-list following do is executed. If in w1 w2 ... is omitted then the loop is executed once for each positional parameter; that is, in $* is assumed.for name in w1 w2 ... do command-list done
Another example of the use of the for loop is the create command whose text is
The commandfor i do >$i; done
ensures that two empty files alpha and beta exist and are empty. The notation >file 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 done.create alpha beta
is an append command. When called with one argument ascase $# in 1) cat >>$1 ;; 2) cat >>$2 <$1 ;; *) echo \'usage: append [ from ] to\' ;; esac
$# is the string 1 and the standard input is copied onto the end of file using the cat command.append file
appends the contents of file1 onto file2. If the number of arguments supplied to append is other than 1 or 2 then a message is printed indicating proper usage.append file1 file2
The general form of the case command is
The shell attempts to match word with each pattern, in the order in which the patterns appear. If a match is found the associated command-list is executed and execution of the case is complete. Since * is the pattern that matches any string it can be used for the default case.case word in pattern) command-list;; ... esac
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 * will never be executed.
Another example of the use of the case construction is to distinguish between different forms of an argument. The following example is a fragment of a cc command.case $# in *) ... ;; *) ... ;; esac
To allow the same commands to be associated with more than one pattern the case command provides for alternative patterns separated by a |. For example,for i do case $i in -[ocs]) ... ;; -*) echo \'unknown flag $i\' ;; *.c) /lib/c0 $i ... ;; *) echo \'unexpected argument $i\' ;; esac done
is equivalent tocase $i in -x|-y) ... esac
The usual quoting conventions apply so thatcase $i in -[xy]) ... esac
will match the character ?.case $i in \?) ...
In this example the shell takes the lines between <<! and ! as the standard input for grep. The string ! is arbitrary, the document being terminated by a line that consists of the string following <<.for i do grep $i <<! ... fred mh0123 bert mh0789 ... ! done
Parameters are substituted in the document before it is made available to grep as illustrated by the following procedure called edg.
The called $3 <<% g/$1/s//$2/g w %
is then equivalent to the commandedg string1 string2 file
and changes all occurrences of string1 in file to string2. Substitution can be prevented using \ to quote the special character $ as ined file <<% g/string1/s//string2/g w %
(This version of edg is equivalent to the first except that ed will print a ? if there are no occurrences of the string $1.) Substitution within a here document may be prevented entirely by quoting the terminating string, for example,ed $3 <<+ 1,\$s/$1/$2/g w +
The document is presented without modification to grep. If parameter substitution is not required in a here document this latter form is more efficient.grep $i <<\# ... #
which assigns values to the variables user, box and acct. A variable may be set to the null string by saying, for example,user=fred box=m000 acct=mh0000
The value of a variable is substituted by preceding its name with $; for example,null=
will echo fred.echo $user
Variables may be used interactively to provide abbreviations for frequently used strings. For example,
will move the file pgm from the current directory to the directory /usr/fred/bin. A more general notation is available for parameter (or variable) substitution, as in,b=/usr/fred/bin mv pgm $b
which is equivalent toecho ${user}
and is used when the parameter name is followed by a letter or digit. For example,echo $user
will direct the output of ps to the file /tmp/psa, whereas,tmp=/tmp/ps ps a >${tmp}a
would cause the value of the variable tmpa to be substituted.ps a >$tmpa
Except for $? the following are set initially by the shell. $? is set after executing each command.
ps a >/tmp/ps$$ ... rm /tmp/ps$$
MAIL=/usr/mail/fred
makes the current directory /usr/fred/bin.cd /usr/fred/bin
will print on the terminal the file wn in this directory. The command cd with no argument is equivalent tocat wn
This variable is also typically set in the the user's login profile.cd $HOME
specifies that the current directory (the null string before the first :), /usr/fred/bin, /bin and /usr/bin 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 / then this directory search is not used; a single attempt is made to execute the command.PATH=:/usr/fred/bin:/bin:/usr/bin
returns zero exit status if file exists and non-zero exit status otherwise. In general test evaluates a predicate and returns the result as its exit status. Some of the more frequently used test arguments are given here, see test (1) for a complete specification.test -f file
The value tested by the while command is the exit status of the last simple command following while. Each time round the loop command-list1 is executed; if a zero exit status is returned then command-list2 is executed; otherwise, the loop terminates. For example,while command-list1 do command-list2 done
is equivalent towhile test $1 do ... shift done
shift is a shell command that renames the positional parameters $2, $3, ... as $1, $2, ... and loses $1.for i do ... done
Another kind of use for the while/until loop is to wait until some external event occurs and then run some commands. In an until loop the termination condition is reversed. For example,
will loop until file exists. Each time round the loop it waits for 5 minutes before trying again. (Presumably another process will eventually create the file.)until test -f file do sleep 300; done commands
that tests the value returned by the last simple command following if.if command-list then command-list else command-list fi
The if command may be used in conjunction with the test command to test for the existence of a file as in
An example of the use of if, case and for constructions is given in section 2.10.if test -f file then process file else do something else fi
A multiple test if command of the form
may be written using an extension of the if notation as,if ... then ... else if ... then ... else if ... ... fi fi fi
The following example is the touch command which changes the `last modified' time for a list of files. The command may be used in conjunction with make (1) to force recompilation of a list of files.if ... then ... elif ... then ... elif ... ... fi
The -c 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 flag is set to some non-null string if the -c argument is encountered. The commandsflag= for i do case $i in -c) flag=N ;; *) if test -f $i then ln $i junk$$; rm junk$$ elif test $flag then echo file \'$i\' does not exist else >$i fi esac done
make a link to the file and then remove it thus causing the last modified date to be updated.ln ...; rm ...
The sequence
may be writtenif command1 then command2 fi
Conversely,command1 && command2
executes command2 only if command1 fails. In each case the value returned is that of the last simple command executed.command1 || command2
and{ command-list ; }
In the first command-list is simply executed. The second form executes command-list as a separate process. For example,( command-list )
executes rm junk in the directory x without changing the current directory of the invoking shell.(cd x; rm junk )
The commands
have the same effect but leave the invoking shell in the directory x.cd x; rm junk
(v 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 sayingset -v
where proc is the name of the shell procedure. This flag may be used in conjunction with the -n flag which prevents execution of subsequent commands. (Note that saying set -n at a terminal will render the terminal useless until an end- of-file is typed.)sh -v proc ...
The command
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 sayingset -x
and the current setting of the shell flags is available as $-.set -
In the first the manual section for sh is printed. Since no section is specified, section 1 is used. The second example will typeset (-t option) the manual section for ed. The last prints the fork manual page from section 2.$ man sh $ man -t ed $ man 2 fork
Figure 1. A version of the man commandcd /usr/man : 'colon is the comment command' : 'default is nroff ($N), section 1 ($s)' N=n s=1 for i do case $i in [1-9]*) s=$i ;; -t) N=t ;; -n) N=n ;; -*) echo unknown flag \'$i\' ;; *) if test -f man$s/$i.$s then ${N}roff man0/${N}aa man$s/$i.$s else : 'look through all manual sections' found=no for j in 1 2 3 4 5 6 7 8 9 do if test -f man$j/$i.$j then man $j $i found=yes fi done case $found in no) echo \'$i: manual page not found\' esac fi esac done
will execute command with user set to fred. The -k flag causes arguments of the form name=value to be interpreted in this way anywhere in the argument list. Such names are sometimes called keyword parameters. If any arguments remain they are available as positional parameters $1, $2, ....user=fred command
The set command may also be used to set positional parameters from within a procedure. For example,
will set $1 to the first file name in the current directory, $2 to the next, and so on. Note that the first argument, -, ensures correct treatment when the first file name begins with a -.set - *
marks the variables user and box for export. When a shell procedure is invoked copies are made of all exportable variables for use within the invoked procedure. Modification of such variables within the procedure does not affect the values in the invoking shell. It is generally true of a shell procedure that it may not modify the state of its caller without explicit request on the part of the caller. (Shared file descriptors are an exception to this rule.)export user box
Names whose value is intended to remain constant may be declared readonly. The form of this command is the same as that of the export command,
Subsequent attempts to set readonly variables are illegal.readonly name ...
orecho $d
will echo nothing. A default string may be given as inecho ${d}
which will echo the value of the variable d if it is set and `.' otherwise. The default string is evaluated using the usual quoting conventions so thatecho ${d-.}
will echo * if the variable d is not set. Similarlyecho ${d-'*'}
will echo the value of d if it is set and the value (if any) of $1 otherwise. A variable may be assigned a default value using the notationecho ${d-$1}
which substitutes the same string asecho ${d=.}
and if d were not previously set then it will be set to the string `.'. (The notation ${...=...} is not available for positional parameters.)echo ${d-.}
If there is no sensible default then the notation
will echo the value of the variable d if it has one, otherwise message is printed by the shell and execution of the shell procedure is abandoned. If message is absent then a standard message is printed. A shell procedure that requires some parameters to be set might start as follows.echo ${d?message}
Colon (:) is a command that is built in to the shell and does nothing once its arguments have been evaluated. If any of the variables user, acct or bin are not set then the shell will abandon execution of the procedure.: ${user?} ${acct?} ${bin?} ...
is equivalent tod=`pwd`
The entire string between grave accents (`...`) is taken as the command to be executed and is replaced with the output from the command. The command is written using the usual quoting conventions except that a ` must be escaped using a \. For example,d=/usr/fred/bin
is equivalent tols `echo "$1"`
Command substitution occurs in all contexts where parameter substitution occurs (including here documents) and the treatment of the resulting text is the same in both cases. This mechanism allows string processing commands to be used within shell procedures. An example of such a command is basename which removes a specified suffix from a string. For example,ls $1
will print the string main. Its use is illustrated by the following fragment from a cc command.basename main.c .c
that sets B to the part of $A with the suffix .c stripped.case $A in ... *.c) B=`basename $A .c` ... esac
Here are some composite examples.
Commands are parsed initially according to the grammar given in appendix A. Before a command is executed the following substitutions occur.
will echo $y.echo $X
will pass on the null string as the first argument to echo, whereasecho ''
will call echo with no arguments if the variable null is not set or set to the null string.echo $null
As well as the quoting mechanisms described earlier using \ and '...' a third quoting mechanism is provided using double quotes. Within double quotes parameter and command substitution occurs but file name generation and the interpretation of blanks does not. The following characters have a special meaning within double quotes and may be quoted using \.
will pass the value of the variable x as a single argument to echo. Similarly,echo "$x"
will pass the positional parameters as a single argument and is equivalent toecho "$*"
The notation $@ is the same as $* except when it is quoted.echo "$1 $2 ..."
will pass the positional parameters, unevaluated, to echo and is equivalent toecho "$@"
The following table gives, for each quoting mechanism, the shell metacharacters that are evaluated.echo "$1" "$2" ...
In cases where more than one evaluation of a string is required the built-in command eval may be used. For example, if the variable X has the value $y, and if y has the value pqr thenFigure 2. Quoting mechanismsmetacharacter \ $ * ` " ' ' n n n n n t ` y n n t n n " y y n y t n t terminator y interpreted n not interpreted
will echo the string pqr.eval echo $X
In general the eval command evaluates its arguments (as do all commands) and treats the result as input to the shell. The input is read and the resulting command(s) executed. For example,
is equivalent towg=\'eval who|grep\' $wg fred
In this example, eval is required since there is no interpretation of metacharacters, such as |, following substitution.who|grep fred
Execution of a command (see also 3.7) may fail for any of the following reasons.
Those signals marked with an asterisk produce a core dump if not caught. However, the shell itself ignores quit which is the only external signal that can cause a dump. The signals in this list of potential interest to shell programs are 1, 2, 3, 14 and 15.Figure 3. UNIX signals
- 1
- hangup
- 2
- interrupt
- 3*
- quit
- 4*
- illegal instruction
- 5*
- trace trap
- 6*
- IOT instruction
- 7*
- EMT instruction
- 8*
- floating point exception
- 9
- kill (cannot be caught or ignored)
- 10*
- bus error
- 11*
- segmentation violation
- 12*
- bad argument to system call
- 13
- write on a pipe with no one to read it
- 14
- alarm clock
- 15
- software termination (from kill (1))
sets a trap for signal 2 (terminal interrupt), and if this signal is received will execute the commandstrap 'rm /tmp/ps$$; exit' 2
exit is another built-in command that terminates execution of a shell procedure. The exit is required; otherwise, after the trap has been taken, the shell will resume executing the procedure at the place where it was interrupted.rm /tmp/ps$$; exit
UNIX signals can be handled in one of three ways. They can be ignored, in which case the signal is never sent to the process. They can be caught, in which case the process must decide what action to take when the signal is received. Lastly, they can be left to cause termination of the process without it having to take any further action. If a signal is being ignored on entry to the shell procedure, for example, by invoking it in the background (see 3.7) then trap commands (and the signal) are ignored.
The use of trap is illustrated by this modified version of the touch command (Figure 4). The cleanup action is to remove the file junk$$.
The trap command appears before the creation of the temporary file; otherwise it would be possible for the process to die without removing the file.Figure 4. The touch commandflag= trap 'rm -f junk$$; exit' 1 2 3 15 for i do case $i in -c) flag=N ;; *) if test -f $i then ln $i junk$$; rm junk$$ elif test $flag then echo file \'$i\' does not exist else >$i fi esac done
Since there is no signal 0 in UNIX it is used by the shell to indicate the commands to be executed on exit from the shell procedure.
A procedure may, itself, elect to ignore signals by specifying the null string as the argument to trap. The following fragment is taken from the nohup command.
which causes hangup, interrupt, quit and kill to be ignored both by the procedure and by invoked commands.trap '' 1 2 3 15
Traps may be reset by saying
which resets the traps for signals 2 and 3 to their default values. A list of the current values of traps may be obtained by writingtrap 2 3
The procedure scan (Figure 5) is an example of the use of trap where there is no exit in the trap command. scan takes each directory in the current directory, prompts with its name, and then executes commands typed at the terminal until an end of file or an interrupt is received. Interrupts are ignored while executing the requested commands but cause termination when scan is waiting for input.trap
read x is a built-in command that reads one line from the standard input and places the result in the variable x. It returns a non-zero exit status if either an end-of-file is read or an interrupt is received.Figure 5. The scan commandd=`pwd` for i in * do if test -d $d/$i then cd $d/$i while echo "$i:" trap exit 2 read x do trap : 2; eval $x; done fi done
The trap turns off the signals specified so that they are ignored by subsequently created commands and exec replaces the shell by the command specified.trap \'\' 1 2 3 15 exec $*
Most forms of input output redirection have already been described. In the following word is only subject to parameter and command substitution. No file name generation or blank interpretation takes place so that, for example,
will write its output into a file whose name is *.c. Input output specifications are evaluated left to right as they appear in the command.echo ... >*.c
runs a command with message output (file descriptor 2) directed to file.... 2>file
runs a command with its standard output and message output merged. (Strictly speaking file descriptor 2 is created by duplicating file descriptor 1 but the effect is usually to merge the two streams.)... 2<&1
The environment for a command run in the background such as
is modified in two ways. Firstly, the default standard input for such a command is the empty file /dev/null. This prevents two processes (the shell and the command), which are running in parallel, from trying to read the same input. Chaos would ensue if this were not the case. For example,list *.c | lpr &
would allow both the editor and the shell to read from the same input at the same time.ed file &
The other modification to the environment of a background command is to turn off the QUIT and INTERRUPT signals so that they are ignored by the command. This allows these signals to be used at the terminal without causing background commands to terminate. For this reason the UNIX convention for a signal is that if it is set to 1 (ignored) then it is never changed even for a short time. Note that the shell command trap has no effect for an ignored signal.
I would like to thank Dennis Ritchie and John Mashey for many discussions during the design of the shell. I am also grateful to the members of the Computing Science Research Center and to Joe Maranzano for their comments on drafts of this document.
if then else elif fi case in esac for while until do done { }