As a UNIX user, you have a choice of shells available to you. These are the Bourne shell, the C shell, and the Korn shell. The C shellthe subject of this chapteris one of the more popular shells available in UNIX. Chronologically, it was
developed after the Bourne shell and before the Korn shell. The C shell incorporates many features of the Bourne shell and adds many new ones that make your UNIX sessions more efficient and convenient.
There are advantages and disadvantages to each shell. You may wish to review Chapter 14, "Which Shell Is Right for You?" to help you decide which one to use.
The Bourne and Korn shells were created at AT&T's Bell Labs, which, not coincidentally, is also where UNIX originated. Bell Labs is not the only organization that contributed to the development of UNIX, however. The Department of Computer Science at
the Berkeley campus, University of California, played a very important role.
As you might have already read, the early versions of UNIX were made available only to colleges and universities under a rather restrictive licensing arrangement: UNIX could be used outside of AT&T only for "research purposes." At
Berkeley, interest in UNIX was very high. The computer science labs added many new features to UNIX and offered their version to other universities as well. The Berkeley version soon became the more popular version, not only because of its many new
features and extensions, but also because Berkeley, unlike Bell Labs, offered maintenance and support to other user groups. Given this fact, it shouldn't be surprising that by the late 1970s, the BSD version (Berkeley Software Distribution) was the
dominant variant of UNIX in use.
One of the additions to UNIX was a new shell, written by Bill Joy (also the author of the vi text editor). Joy did not pattern his shell after the Bourne shell; indeed, to judge by results, he apparently felt that the Bourne shell syntax was clumsy and
nonintuitive. As a syntax model, he chose the C programming language. The C shell commands, especially if, while, and the other structured programming statements, are somewhat similar in syntax to the equivalent statements in C. A shell is quite a
different animal from a compiler, however, so the C programming language served only as a model; many forms and structures in the C shell have nothing to do with the C programming language.
Because the C shell is not just an extension of the Bourne shell syntax, this chapter will cover all aspects of C shell operation; it can therefore be read independently from Chapter 11, "Bourne Shell," and Chapter 12, "Korn Shell."
Each time you log in to UNIX, you're placed in an interactive shell referred to as your login shell. If your login shell is C shell, you can tell by its command-line prompt: the percent sign (%). The C shell prompt differs from the dollar sign prompt
($) of the Bourne shell to remind you that you're using the C shell. You can customize your keyboard prompt when using the C shell; for more information see the definition of prompt in the section titled "Variables" later in this chapter.
If your login shell is not C shell, and C shell is available on your system, you can invoke it as an interactive shell from the command line. Even when you're already running the C shell, there will be times when you want to launch the C shell again,
for example to run a shell script or to temporarily change the shell's options. To invoke the C shell interactively, use the following command:
$ csh %
The csh command also supports a number of options and arguments (described later in this chapter in the section titled "Shell Options"), but most of them are not relevant to running an interactive shell.
Whenever csh is invoked, whether as the login shell or as a subshell, it loads and executes a profile script named .cshrc. If it is a login shell, the C shell will also execute a profile script on startup named .login, and another on exit named .logout.
Note that the .login script is executed after .cshrc, not before. For additional information about C shell profile scripts, see the section titled "Customizing Your Shell Environment" later in this chapter.
Most versions of the C shell import environment variables such as PATH into local array variables at startup. The C shell does not refer to the public environment variables (including PATH) for its own operation. This means that usually
you'll want to maintain the path variable for directory searches, not PATH. Some versions of the C shell do not properly import environment variables, with confusing results. If it appears that you have no search path set, but the PATH
variable is set and accurate (as shown by echo $PATH), check that the variable path has a matching value. If not, you'll need to import critical environment variables into local variables yourself.
When you enter commands at the shell prompt, you are providing input to the shell. The shell sees a line of input as a string of characters terminated with a newline character that is usually the result of pressing return on your keyboard. That input
can be anything from a single, simple command to multiple commands joined with command operators. Each command line that you enter is actually a shell statement. In addition to providing input to the shell manually by entering shell statements on the
command line, you can also provide input to the shell by putting shell statements into a file and executing the file.
The next section covers the basics of interacting with the shell by entering shell statements on the command line. (Of course, anything that you can enter on the command line can also be put into a file for later, "canned" execution. Such
files are called shell scripts.) The section following is titled "Shell StatementsA Closer Look," which provides a more detailed, technical look at components of shell statements. If you plan to write shell scripts, you'll definitely want
to read this section.
When you finish this section, you will feel like you know a lot about the shell, but this is just the beginning. In addition to its basic service of providing a means to instruct the computer, the shell also provides a number of tools you can use to
expedite your work flow. These tools, or features of the shell, are described in subsequent sections of this chapter.
C shell accepts several types of commands as input: UNIX commands, built-in shell commands, user-written commands, and command aliases. This section describes the different types of commands you can execute and the various ways you can execute commands.
As you know, you execute a command by entering the command's name. The C shell supports any of the following as command names:
C shell provides a number of commands implemented within the shell program itself. Built-in commands execute very quickly because no external program file needs to be loaded. Table 13.1 lists the commands alphabetically along with a brief description of
each one. The remainder of this chapter groups these commands into subsections dedicated to particular tasks you'll perform in the shell and describes how to use each command.
Command |
Description |
|
alias |
Define or list a command alias |
|
bg |
Background execution |
|
break |
Breaking out of a loop |
|
breaksw |
Exit from a switch statement |
|
case |
Begin a case in switch |
|
cd |
Change directory |
|
chdir |
Change directory |
|
continue |
Begin the next loop iteration immediately |
|
default |
Label for the default case in switch |
|
dirs |
List the directory stack |
|
echo |
Echo arguments to standard output |
|
eval |
Rescan a line for substitutions |
|
exec |
Invoke a new shell |
|
exit |
Exit from the current shell |
|
fg |
Switch a job to foreground execution |
|
foreach |
Looping control statement |
|
glob |
Echo arguments to standard output |
|
goto |
Alter the order of command execution |
|
hashstat |
Print hash table statistics |
|
history |
List command history |
|
if |
Conditional execution |
|
jobs |
List active jobs |
|
kill |
Signal a process |
|
limit |
Respecify maximum resource limits |
|
login |
Invoke the system login procedure |
|
logout |
Exit from a login shell |
|
newgrp |
Change your group ID |
|
nice |
Control background process dispatch priority |
|
nohup |
Prevent termination on logout |
|
notify |
Request notification of background job status changes |
|
onintr |
Process interrupt within a shell script |
|
popd |
Return to a previous directory |
|
pushd |
Change directory with pushdown stack |
|
rehash |
Rehash the directory search path |
|
repeat |
Repetitively execute a command |
|
set |
Display or change a variable |
|
setenv |
Set environment variable |
|
shift |
Shift parameters |
|
source |
Interpret a script in the current shell |
|
stop |
Stop a background job |
|
suspend |
Stop the current shell |
|
switch |
Conditional execution |
|
time |
Time a command |
|
umask |
Display or set the process file creation mask |
|
unalias |
Delete a command alias |
|
unhash |
Disable use of the hash table |
|
unlimit |
Cancel a previous limit command |
|
unset |
Delete shell variables |
|
unsetenv |
Delete environment variables |
|
wait |
Wait for background jobs to finish |
|
while |
Looping control |
|
%job |
Foreground execution |
|
@ |
Expression evaluation |
The most common form of input to the shell is the simple command, where a command name is followed by any number of arguments. For example, in the following command line
% chdir dirname
chdir is the command and dirname is the argument. It is the responsibility of the command, not the shell, to interpret the arguments. Many commands, but certainly not all, take the form
% command -options filenames
Although the shell does not interpret the arguments of the command, the shell does make some interpretation of the input line before passing the arguments to the command. Special characters entered on a command line cause the shell to redirect input and
output, start a different command, search the directories for filename patterns, substitute variable data, and substitute the output of other commands.
Ordinarily, the shell interprets the first word of command input as the command name and the rest of the input as arguments to that command. The semicolon (;) directs the shell to interpret the word following the symbol as a new command, with the rest
of the input as arguments to the new command. For example, the command line
% who -H; df -v; ps -e
is the equivalent of
% who -H % df -v % ps -e
except that in the second case the results of each command would appear between the command input lines.
When the semicolon is used to separate commands on a line, the commands are executed in sequence. The shell waits until one command is complete before executing the next. You can also execute commands simultaneously (see the section titled
"Executing Commands in the Background") or execute them conditionally, which means that the shell executes the next command only if the first command succeeds or fails (see the section titled "Executing Commands Conditionally").
Sometimes command lines get quite lengthy. On some terminals, when you reach the edge of the display screen the input autowraps to the next line, but depending on terminal settings, some do not. It would be nice if you could type part of a command on
one line and enter the remainder of the command on a second line. This can be accomplished by escaping the newline character.
Remember that the shell sees a line of input as a statement terminated with a newline character. But the newline character is also considered to be a white space character. If you end a line with a backslash (\), the next characterthe newline
characterwill be treated literally, meaning that the shell will not interpret the newline character as the end of the line of input.
% echo Now is the time for all good men \_ to come to the aid of the party. Now is the time for all good men to come to the aid of the party.
Normally when you execute commands, they are executed in the foreground. This means that the command has the system's undivided attention, and you can't do anything else until the command finishes executing. For commands that take a long time to
execute, however, this can be a problem. To free your system without waiting for the command to finish, you can execute the command in the background by putting an ampersand (&) at the end of the command:
% who -H & [1] + Running who -H & %
You also can run multiple commands in the background simultaneously:
% who -H & df -v & ps -e &
A command executing in the background is referred to as a job, and each job is assigned a job numberthe bracketed number in the preceding example. C shell provides you with several commands for managing background jobs; see the section later in
this chapter titled "Job Control."
You can use the repeat command to execute some other command a specified number of times. While the repeat command doesn't see frequent use, it can on occasion be quite handy. For example, if you had stored some text in a model file, and wanted to make
five copies of it, you could do so easily with the command
repeat 5 cat model.txt new.txt
Or, if you were writing a shell script to print a document, you might use the command
repeat 5 echo *******************************
to mark its first page clearly as the start of the document.
The syntax of the repeat command is as follows:
repeat count command
For count, specify a decimal integer number. A count of zero is valid and suppresses execution of the command.
For command, specify a simple command that is subject to the same restrictions as the first format of the if statement. The command is scanned for variable, command, and history substitutions, filename patterns, and quoting. It cannot be a compound
command, a pipeline, a statement group (using {}), or a parenthesized command list.
Any I/O redirections are performed only once regardless of the value of count. For example, repeat 10 echo Hello >hello.list would result in ten lines of Hello in a file named hello.list.
A command (or a list of commands separated with semicolons) enclosed in parentheses groups the command or commands for execution in a subshell. A subshell is a secondary invocation of the shell, so any change to shell variables, the current directory,
or other such process information lasts only while executing the commands in the group. This is a handy way, for example, to switch to another directory, execute a command or two, and then switch back without having to restore your current directory:
% (cd /home/bill; cp *.txt /home/john)
Without the parentheses, you would have to write:
% cd /home/bill % cp *.txt /home/john % cd /home/john
The syntax for grouping commands is:
( commands )
Enclosing a list of commands in parentheses is a way to override the default precedence rules for the &&, ||, and | operators, at the expense of invoking a subshell and losing any environmental effects of the commands' execution. For example,
(grep || echo) | pr will pipe the output of the grep command, and possibly that of echo if grep sets a nonzero exit code, to the pr command.
I/O redirections can be appended to the subshell just as for a simple command; the redirections are in effect for all of the commands within the subshell. For example, (cat; echo; date) > out will write the output of the cat, echo, and date commands
to a file named out without any breaks. If you look at the file afterward, first you'll see the lines written by cat, followed by the lines written by echo, and finally the lines written by date. Similarly, input redirections apply to all commands in the
subshell, so that each command in turn reads lines from the redirected file, starting with the line following those read by any previously executed commands in the subshell.
Compound commands are actually two or more commands combined together so that the shell executes all of them before prompting (or, in the case of shell scripts, reading) more input.
Compound commands are not often needed for work at the keyboard, and you'll rarely feel the lack if you don't understand or don't use compound commands. However, compound commands form a very useful extension to the shell's syntax, especially in shell
scripts. Some compound command formats, such as & (background job) and | (the pipe operator) are essential to effective work with UNIX.
The double ampersand operator (read and) is used to join two commands: command1 && command2. It causes the shell to execute command2 only if command1 is successful (has a zero exit code).
For command1 or command2, you can write a simple command or a compound command. The && operator has higher precedence than || but lower precedence than |. For example,
grep '#include' *.c | pr && echo OK
will echo OK only if the pipeline grep | pr sets a zero exit code. (For pipelines, the exit code is the exit code of the last command in the pipeline.)
The compound command cp file1.c file1.bak && rm file1.c shows the possible benefit of using &&: The rm command will delete file1.c only if it is first successfully copied to file1.bak.
The or operator is used to join two commands: command1 || command2. It causes the shell to execute command2 only if command1 failed (set a nonzero exit code).
For command1 or command2, you can write a simple command or a compound command. The || operator has lower precedence than both the && and | operators. For example, in the following command
grep '#include' *.c || echo No files | pr
either grep succeeds, or else the words No files are piped to the pr command. That is, the pipe is between the echo and pr commands, not between grep (or grep || echo) and pr.
Use the || operator to provide an alternative action. For example, in the following case, if the mkdir command fails, the exit command prevents further execution of the shell script:
mkdir $tmpfile || exit
A command is either a basic command, or a basic command embellished with one or more I/O redirections.
A basic command is a series of words, each subject to replacements, which when fully resolved specifies an action to be executed and provides zero or more options and arguments to modify or control the action taken. The first word of a basic command,
sometimes called the command name, must specify the required action.
In plainer terms, a statement is the smallest executable unit. When the shell is operating in interactive mode, it displays its prompt when it requires a statement. You must continue to enter shell statement components, using multiple lines if
necessary, until you have completed a full statement. If the statement is not completed on one line, the shell will continue to prompt you, without executing the line or lines you have entered, until it has received a full statement.
Shell statements are formed from a number of tokens. A token is a basic syntactic element and can be any of the following:
Filename generation using patterns is an important facility of the Bourne shell. The C shell supports the filename patterns of the Bourne shell and adds the use of {} (braces) to allow greater flexibility.
Several shell commands and contexts allow the use of pattern-matching strings, such as the case statement of switch and the =~ and !~ expression operators. In these cases, pattern strings are formed using the same rules as for filename generation,
except that the patterns are matched to another string.
When any of the pattern expressions described below are used as arguments of a command, the entire pattern string is replaced with the filenames or pathnames that match the pattern. By default, the shell searches the current directory for matching
filenames, but if the pattern string contains slashes (/), it searches the specified directory or directories instead. Note that several directories can be searched for matching files in a single pattern string: a pattern of the form dir/*/*.c will search
all the directories contained in dir for files ending with .c.
|
* |
The asterisk matches any string of characters, including a null string. Used by itself, it matches all filenames. Used at the beginning of a pattern string, it means that leading prefixes of the filename pattern are ignored: *.c matches any filename ending with .c. Used at the end of a pattern string, it means that trailing suffixes of the filename pattern are ignored: s.* will match s.main, s.prog.c, and any filename beginning with s.. Used in the middle of a pattern, it means that matching filenames must begin and end as shown but can contain any character sequences in the middle: pay*.c matches filenames beginning with pay and ending with .c, such as payroll.c, paymast.c, and paycheck.c. |
|
? |
The question mark matches any one character. For example, ? as a complete word will match all filenames one character long in the current directory. The pattern pay?.c will match pay1.c and pay2.c but not payroll.c. Multiple question marks can be used to indicate a specific number of don't-care positions in the filename: pay??.c will match filenames beginning with pay and containing any two characters before .c, such as pay01.c and paybb.c, but will not match payroll.c. |
|
[] |
The square brackets enclose a list of characters. Matching filenames contain one of the indicated characters in the corresponding position of the filename. For example, [abc]* will match any filename beginning with the letter a, b, or c. Because of the asterisk, the first character can be followed by any sequence of characters. |
|
|
Use a hyphen (-) to indicate a range of characters. For example, pay[1-3].c will match filenames pay1.c, pay2.c, and pay3.c, but not pay4.c or pay11.c. Multiple ranges can be used in a single bracketed list. For example, [A-Za-z0-9]* will match any filename beginning with a letter or a digit. To match a hyphen, list the hyphen at the beginning or end of the character list: [-abc] or [abc-] will match an a, b, c, or hyphen. |
|
|
Use a circumflex (^) after [ to negate the range of characters. The pattern [^a-zA-Z0-9]* will match all filenames that do not begin with a letter or digitthat is, filenames beginning with a punctuation character such as .c or #myfile.txt. |
|
{} |
Braces enclose a list of patterns separated by commas. The brace expression matches filenames having any one of the listed patterns in the corresponding position of the name. For example, the pattern /usr/home/{kookla,fran,ollie}/.profile expands to the path list /usr/home/kookla/.profile /usr/home/fran/.profile /usr/home/ollie/.profile. Unlike *, ?, and [], brace-enclosed lists are not matched against existing filenames; they are simply expanded into filenames regardless of whether the corresponding files exist. Brace-enclosed lists can be nested, for example /usr/{bin,lib,home/{john,bill}} refers to any of the directories /usr/bin, /usr/lib, /usr/home/john, and /usr/home/bill. |
The tilde (~) can be used at the beginning of a word to invoke directory substitution. The tilde forms are as follows:
|
~ |
Substituted with the full pathname of your home directory. Also used in the form ~/path to refer to a file or directory under your home directory. |
|
~name |
Substituted with the full pathname of user name's home directory. For example, ~ken/bin refers to /usr/ken/bin if the home directory for user ken is /usr/ken. The password file /etc/passwd is searched for name to determine the directory pathname; if name is not found, the shell generates an error message and stops. |
If the tilde does not appear by itself as a word, and is not followed by a letter or by a slash, or appears in any position other than the first, it is not replaced. Thus, /usr/marta/~file.c is a reference to the file ~file.c in the directory
/usr/marta.
It is important to realize that filename generation using pattern strings causes a replacement of one word with many. A filename pattern must be a single word. The ordinary characters and pattern-matching characters in the word describe a rule for
choosing filenames from the current or specified directory. The word is replaced with each filename or pathname found that matches the pattern. Consider the following examples:
% echo Files: *.txt Files: ch1.txt ch2.txt chlast.txt % set files=(*.txt) % echo Found $#files files Found 3 files % echo $files[2] ch2.txt mkdir $tmpfile || exit
C shell provides several commands for redirecting the input and output of commands. You might already be familiar with the input (<) or output (>) redirection characters from earlier chapters. C shell provides you with these and more.
An I/O redirection is an instruction to the shell you append to a command. It causes one of the standard file descriptors to be assigned to a specific file. You might have previously encountered standard files in the discussion of the Bourne shell
(Chapter 11). The UNIX operating system defines three standard file descriptors: standard input, standard output, and standard error. (These names are sometimes abbreviated to stdin, stdout, and stderr.)
A file descriptor is not the file itself. Rather, it is a channel, much like the phone jack on the back of your stereo: you can connect it to any audio source you like. Similarly, a file descriptor such as standard input must be connected to a
fileyour terminal by default, or the disk file or readable device of your choice.
You can change the location where a command reads data, writes output, and prints error messages, using one or more of the I/O redirection operators. The operators are shown in Table 13.2.
Format |
Effect |
|
Input Redirection | |
|
< filename |
Use the contents of filename as input to a command. |
|
<< word |
Provide shell input lines as command input. Lines of the shell input which follow the line containing this redirection operator are read and saved by the shell in a temporary file. Reading stops when the shell finds a line beginning with word. The saved lines then become the input to the command. Of course, the lines read and saved are effectively deleted from the shell input, and will not be executed as commands; they are effectively "eaten" by the << operator. Shell execution continues with the line following the line beginning with word. If you use the << operator on a command you type at the terminal, be careful: lines you type afterward will be gobbled up by the shellnot executeduntil you enter a line begining with whatever you specified as word. The << operator is most often used in shell scripts. |
|
| |
|
> filename |
Write command output to filename |
|
>! filename |
Write command output to filename, and ignore the noclobber option. The noclobber option is fully explained in the section "Using Predefined Variables" later in this chapter. Briefly it causes the shell to disallow the > filename redirection when filename already exists; noclobber is therefore a safety you can use to prevent your accidentally destroying an existing file. Of course, sometimes you want to redirect output to a file even though it already exists. In such a case, you must use the >! operator to tell the shell you really want to proceed with the redirection. If you don't set the noclobber option, then you won't need to use the >! operator either. |
|
>& filename |
Open filename and write both the command output and error messages to it |
|
>&! filename |
Open filename and write both the command output and error messages to it, and ignore the noclobber option |
|
>> filename |
Open filename and write command output at the end of the file (append mode) |
|
>>! filename |
Open filename and write command output at the end of the file (append mode), and ignore the noclobber option |
|
>>& filename |
Open filename and write command output and error messages at the end of the file (append mode) |
|
>>&! filename |
Open filename and write command output and error messages at the end of the file (append mode), and ignore the noclobber option |
In Table 13.2, filename represents any ordinary filename or pathname, or any filename or pathname resulting after variable substitution, command substitution, or filename generation.
I/O redirection operators are appended to a command; for example, date >curdate will write the current date to the file curdate instead of to your terminal. You can also use more than one redirection per command: simply list them one after another at
the end of the command. The order doesn't matter: for example, both cat <infile >outfile and cat >outfile <bigfile will have the same effect.
Some commands make no special use of the standard input file, such as the date and the ls system commands; others require an input file to function properly, such as the cat and awk commands. You can use the < redirection operator in the form command
< filename to designate a file as the source of input for commands like cat and awk; if you do not, these commands will read data from your keyboardsometimes useful, but usually not. If you provide an input redirection, but the command does
not read data (such as ls), the I/O redirection is still performed by the shell, it is just ignored by the command.
It is an error to redirect standard input to a file that doesn't exist.
The redirection << word is a special form of the input redirection operator. Rather than taking input from a file, input to the command comes from the current shell input streamyour keyboard, if you append << to a command you
type in, or your shell script if you use << on a command in a shell script.
For word, you choose an arbitrary string to delimit the lines of input. Then write the lines to be provided to the command as input immediately following the command line, and follow the last line with a line beginning with word. The shell reads the
lines ahead, stores them in a temporary file, and sets up the temporary file as standard input for the command.
This form of input redirection is called a here document, because it is located here, in line with your shell commands. It is useful when you want to provide predefined data to a command, and it saves you from having to create a file to hold the data.
Unlike the filename part of other I/O redirection operators, word for the here document is not scanned for variable references, command substitutions, or filename patterns; it is used as is. Also, the following shell input lines are checked for
the presence of word as the first word of the line before any substitutions or replacements are performed on the line.
Normally, lines of the here document are checked for variable references and command replacements; this allows you to encode variable information in the here document. If you quote any part of word, however, the lines are read and passed to the command
without modification. For example, the redirection << STOP reads lines up to STOP, and performs substitutions on the lines it reads; the redirection << "STOP" reads lines up to the line beginning with STOP, and passes the lines
directly to the command, as is, without substitutions or replacements of any kind.
The line beginning with word is discarded, and neither passed to the command in the here document, nor executed by the shell.
The following example shows the use of a here document to print a customized message:
pr << HERE | lp Hello, $user. Your print job, 'lpstat' has been scheduled for output at a later time. Please contact Joe if you have any questions. HERE
The line containing the word HERE will not appear in the output message; it is simply a mark to let the shell know where the redirected lines end.
Output redirections have the general form > and >>. The first operator creates a new file of the specified name. The file is opened before command execution begins, so even if the command fails, or cannot be found, or if the shell finds an
error on the command line and stops, the output file will still be created.
If you've set the noclobber option (with set noclobber), then the shell will refuse to create the named output file if it already exists; doing so would destroy its current contents. If you want to perform the output redirection even if the file
filename already exists, use the redirection operator >! instead; it overrides the noclobber option.
The >> command arranges for command output to be added to the end of the named file. For this redirection operator, the noclobber option requires that the named file already exist. If you use the alternate form >>!, or if you use >>
and the noclobber option is not set, the shell will create the named file if necessary.
The >& and >>& operators redirect both the standard output and standard error files to filename. The Bourne shell allows you to redirect the standard output and standard error files separately; the C shell does not. Actually,
this is not much of a limitation in real life.
If you have the noclobber option set, you'll need to use >&! instead of >& to proceed even if the named file exists, or >>&! to proceed even if the named file doesn't exist.
As you've seen from previous sections, certain characters have special meaning for the shell. That is, when the shell encounters a special character, it will perform the action that the special character calls for. The following punctuation characters
available on the standard keyboard are special to the shell and disrupt the scanning of ordinary words:
~ ' ! @ # $ % ^ & * ( ) \ | { } [ ] ; ' " < > ?
In some contexts, particularly within the switch statement, the : (colon) is also a special character. The colon is recognized as a special character only when expected, in a case or default statement, and as a statement label. It does not need to be
quoted except to avoid these specific interpretations.
To use one of these characters as a part of a word without its special significance, you can escape the character by placing a backslash (\) immediately in front of the character. Note that a backslash intended as an ordinary character must be written
as two backslashes in succession: \\. To escape a two-character operator such as >>, you must insert a backslash in front of each character: \>\>.
Alternatively, you can enclose the special character or any portion of the word containing the character in quotes. The shell recognizes three kinds of quotes: the apostrophe ('), the quote ("), and the backquote (`).
Use two apostrophes (also called single quotes) to enclose a character sequence and avoid all interpretation by the shell. I often call a string enclosed in apostrophes a hard-quoted string, because the shell performs absolutely no substitution,
replacement, or special interpretation of anything appearing between the apostrophes. Even the backslash character is treated as an ordinary character, so there are no escapes within an apostrophe-enclosed string, and you cannot embed an apostrophe in such
a string. That is, the string 'who's there' will cause a shell error: the shell will see this as who concatenated with an s, followed by a white space delimiter, followed by a word beginning with there, and then the starting apostrophe of another string.
The third apostrophe starts a quoted string that the shell will follow over as many lines as necessary to find an ending apostrophe, probably eating up shell lines you intended as commands, and eventually yielding a shell syntax error or an erroneous
command execution.
One of the uses of quoted strings is to specify a single word containing blanks, tabs, and newline characters. For example, the following shows the use of a single echo command to print two lines of output:
% echo -n 'Hello. Please enter your name: ' Hello. Please enter your name:
The double apostrophe or quote (") also provides a special bracket for character strings. Like the apostrophe, the quote hides most special characters from the shell's observation. Quoted strings, however, are subject to two kinds of scan and
replacement: variable references and command substitutions.
Any of the reference forms for shell variables ($1, $name, ${name}, $name[index], $*, and others) are recognized inside quoted strings and are replaced with the corresponding string value. The replacement occurs inside the quoted
string, leaving its unity as a single word intact (even if the substituted value includes blanks, tabs, or newline characters).
Command substitution occurs for strings enclosed in backquotes ('). The entire string enclosed between matching backquotes is extracted and executed by the shell as if it were an independent command. The command can be two or more commands separated
with semicolons, or a pipeline, or any form of compound statement. Any data written to standard output by the command is captured by the shell and becomes the string value of the backquoted command. The string value is parsed into words, and the series of
words replaces the entire backquoted string.
All forms of shell substitution will occur inside backquoted command strings, including variable replacement, nested command executions, history substitutions, and filename patterns. Nested command strings will work, but the backquotes introducing them
must be escaped with \ to hide them from the shell's first scan of the backquoted string.
A backquoted command string (or any number of them) can appear inside a quoted string and will have its normal effect; this is the second form of substitution performed on "-quoted strings. A quoted command substitution
("xxx`commands`xxx") generates new words only at the end of each line, except at the end of the last line. If the executed command prints only one line of text, the text replaces the backquoted expression without introducing
any word breaks.
Both quoting forms '...' and "..." suppress filename generation. For example, note the difference in the following echo commands:
% echo *.c main.c io.c parse.c math.c % echo "*.c" *.c
Apostrophes and quotes can appear inside a double-quoted string. The double quote must be escaped with a backslash to prevent premature termination of the quoted string (for example "He said, \"John!\""). The apostrophe has no
special significance when appearing inside a double-quoted string and does not need to be backslashed. The following example shows the use of quotes inside quoted strings:
% echo "He said, \"John!\"" He said, "John!" % echo "Filename: '$1'" Filename: '/usr/bin/ls'
A backslash appearing inside an apostrophe-quoted string is retained and appears in the string's value, because no substitutions occur inside an apostrophe-quoted string. Inside a double-quoted string or a command substitution using ', or in a normal
unquoted word, a backslash has the effect of suppressing shell interpretation of the character that follows it; the backslash is then removed from the string. The following examples show the effect of a backslash in all these contexts:
% echo "Double \" quote" Double " quote % echo Double \" quote Double " quote % echo 'Single \' quote Single \ quote % echo Single \' quote Single ' quote
C shell provides you with several built-in commands for working with directories. The cd, chdir, pushd, and popd commands all change the current directory in one way or another.
The pushd and popd commands provide a pushdown stack mechanism for changing directories, and the dirs command displays the contents of the stack. If you switch to another directory using pushd instead of cd, the pathname of your previous directory is
"saved" in the directory stack. A subsequent popd will then return you to the previous directory. Be aware that the cd command does not maintain the directory stack; you cannot use popd to return to a directory that you left using cd.
In C shell, you can choose from two commands for changing your current working directory: cd and chdir. The chdir command is equivalent to cd in every way. The syntax for these commands is as follows:
cd [ name ] chdir [ name ]
If you omit the name argument, the command attempts to change to the directory whose pathname is given by the value of the C shell variable home; see the section later in this chapter titled "Using Predefined Variables" for more about
home.
If you specify a name, the cd or chdir command uses a search hierarchy to attempt to locate the referenced directory, as follows:
For more information about the cdpath variable, see the section titled "Using Predefined Variables" later in this chapter.
The cd and chdir commands as implemented by the C shell provide a great deal of flexibility in generating shortcuts for directory names. There is nothing more painful than having to repeatedly type long directory names on the cd command. The purpose of
the cd command's search hierarchy is to provide some mechanisms you can use for shortening a reference to a directory name. The cdpath variable is your principal tool: if you set it to a list of directories you often reference, you can switch to one of
those directories just by giving the base directory name. If cdpath is not sufficiently flexible to suit your needs, you can define a shell variable as an alias for a directory's full pathname, then cd varname will switch you to that
directory for the price of a few keystrokes.
The directory stack is a mechanism by which you can store and recall directories you have changed to using the special change-directory commands pushd and popd, discussed in the next two sections. The dirs command lists the directories in the directory
stack:
% dirs /usr/home/john/bin /usr/home/john /usr/home/john/docs
Three directories are on the directory stack in this example for user john. The first directory listed is the current directory (the one you would see if you entered the pwd command). Directories to the right are previous directories, the farthest to
the right being the least recent. In this example, the directory /usr/home/john/docs was the first directory to be changed tothat is, "pushed" onto the pushdown directory stack, /usr/home/john was the next directory, and /usr/home/john/bin
was the directory most recently changed to (the current directory).
To save the pathname of a directory on the directory stack, use the pushd command to change to another directory. Using pushd saves the pathname of your previous directory on the directory stack so that you can return to the previous directory quickly
and easily using the popd command. Use dirs to display the directories currently saved on the pushdown stack.
There are three forms of the pushd command:
pushd pushd name pushd +n
Used in the form pushd, the command exchanges the top two directory stack elements, making your previous directory the current and your current directory the previous. Successive pushd commands used without an argument therefore switch you back and
forth between the top two directories.
Used in the form pushd name, the command changes to directory name in the same way as cd would have; pushd uses the cdpath directory list to resolve name, and succeeds or fails in the same cases as cd. The pathname of the current directory is
saved in a directory stack prior to the change. The directory stack is an implicit array variable maintained by the shell (which you cannot access directly) so that each pushd adds the current directory on the left and pushes all existing entries to the
right; the top (or first) element is always your current directory, and subsequent entries are the pathnames of your previous directories in reverse order. The popd command discards the top stack entry and changes to the new top entry, reducing the total
number of items stacked by one.
Use the form pushd +n to do a circular shift of the directory stack by n positions, changing to the new top directory. A circular shift treats the list of elements as if they were in a ring, with the first preceded by the last and the last
followed by the first; the shift changes your position in the ring without deleting any of the elements. Consider the following example:
% dirs /home/john /home/mary /home/doggie /home/witherspoon % pushd +2 /home/doggie % dirs /home/doggie /home/witherspoon /home/john /home/mary
Note that both before and after the pushd, /home/john precedes /home/mary, and /home/doggie precedes /home/witherspoon. The example also shows that, for the purpose of the pushd +n command form, /home/witherspoon (the last entry) is effectively
followed by /home/john (the first entry).
After you have saved directories on the directory stack with pushd, you can use popd to return to a previous directory. The syntax for the popd command is as follows:
popd [ +n ]
The following example shows the use of pushd, dirs, and popd together:
% pwd /usr/home/john % pushd /usr/spool % pushd uucppublic % pushd receive % dirs /usr/spool/uucppublic/receive /usr/spool/uucppublic /usr/spool _/usr/home/john % popd /usr/spool/uucppublic % dirs /usr/spool/uucppublic /usr/spool /usr/home/john % popd /usr/spool % dirs /usr/spool /usr/home/john % popd /usr/home/john % dirs /usr/home/john
Used in the form popd +n, the command deletes the nth entry in the stack. Stack entries are numbered from 0, which is your current directory.
The C shell provides a number of commands for changing the active shell. Although your login shell may be the C shell, you are not limited to it; you can change your shell to Bourne shell or the Korn shell at any time using the exec command. The exit
and logout commands also change the active shell, by returning you to the shell that was active before your current shell: issued from your login shell, they return you to the login screen, which is itself a kind of shell (of somewhat limited
functionality).
Other commands, such as umask and nohup, change the manner in which UNIX treats the shell.
In order to make the best use of the information in this section, you should also read Part IV, Process Control, later in this book, which describes some of the UNIX mechanisms these commands are designed to manipulate.
The exec command transfers control to the specified command, replacing the current shell. The command you specify becomes your new current shell. The syntax of the exec command is as follows:
exec command
Nearly always, command should be a shell invocation command such as csh, sh, or ksh. Control cannot be returned to the invoking environment because it is replaced by the new environment. Shell variables exported with the setenv command will be passed to
the new shell in the usual manner; all other command contexts, including local variables and aliases, will be lost.
The exec command is equivalent to the Bourne shell exec.
The exit command causes the current shell invocation to be exited. Its syntax is as follows:
exit [ (exp) ]
If issued from within a shell script, the shell script is terminated and control returns to the invoking shell. If issued from your login shell, the .logout script in your home directory will be executed before the shell exits. Normally, the UNIX
operating system will redisplay a login screen after an exit from the login shell.
If you provide the optional exp argument (which must be enclosed in parentheses), the argument is evaluated as an arithmetic expression, and the resulting value is used as the shell's exit code; otherwise, the current value of the status variable is
taken as the shell's exit code. The status variable is described in the section "Using Predefined Variables" later in this chapter.
Use the login command to log out from your current shell and to immediately log in under the same or a different user ID. Its syntax is as follows:
login name [ arg ... ]
Using this shell built-in command is not quite equivalent to logging out in the normal manner and then logging in. If you use the login command from a remote terminal, the line connection will not be dropped, whereas logging out in the normal manner
drops the line and requires you to re-establish the connection before you can log in again.
You cannot execute the login built-in command from a subshell; it is legal only for your login shell.
For name, specify the user name you want to log in with. Any arguments you specify after name are passed to the /bin/login command and are defined by /bin/login, not by the shell.
Use the logout command to log out from your login shell.
logout
You can also terminate the login shell (or any subshell) with the exit command. If you have the ignoreeof option set, you cannot use the EOF key to exit from the shell; in such a case, use logout or exit. See the section "Using Predefined
Variables" for a definition of the ignoreeof option.
Use the nohup command to run a command that is insensitive to the Hangup signal.
nohup [ command ]
The UNIX operating system always sends a Hangup signal (signal 1) to a process when its process group leader logs out. The net effect is that normally any command you are running when you log out is terminated. (Although you can't ordinarily issue the
logout or exit command, or enter an EOF character, while you are running a command, you can always force a logout by turning off your terminal, or if using a remote terminal connection, by hanging up the line.)
When you invoke a command with nohup, the shell effectively disables the Hangup signal so that the command cannot receive it, thus allowing command to continue to execute after you log out.
You can disable the Hangup signal for your interactive shell or from within a shell script using the trap built-in command, and binary programs written in the C language can also disable or ignore the Hangup signal. However, not all commands do this. If
you use nohup to invoke the command, you are assured that the Hangup signal will be ignored whether or not the command disables the signal.
Use nohup with no arguments from within a shell script to disable the Hangup signal for the duration of the script.
Use nohup command to run command with the signal disabled.
The process file creation mask (sometimes, for purposes of brevity, called the umask), is an attribute of the shell process, just like the current directory is a process attribute. The purpose of the file creation mask is to specify the default
permissions assigned to new files you create, for example when redirecting the output of a command to a file with the > operator. It would be extremely inconvenient if the system prompted you for file permissions every time it created a file, especially
since most of the time you would assign the same permissions to all new files.
If you're not familiar with file permissions, you may want to review the section "File Security" in Chapter 3, "The UNIX File System." Briefly, file permissions are little flags that UNIX associates which each file. The flags
indicate whether the file can be read, written, or executed, and by whom.
The file creation mask is a device you use for indicating what permissions UNIX is to assign to a new file by default. If you want some other access permissions for a file, the usual approach is to first create the file, then change the file's
permissions with the chmod command.
The file creation mask itself is a binary value consisting of nine bits, corresponding to each of the permission bits for a file. As a matter of convention, the nine bits are represented by three octal digits, with each digit representing three bits.
The file creation mask is therefore a value expressed in octal as three octal digits. The use of octal number representation for the file creation mask is a matter of convention, not necessity, yet the umask command does not allow you to use any other
number form for displaying or setting the file creation mask: you must use octal to set the mask, and you must interpret octal values to understand the mask when displayed.
As for the mask itself, each of the bits in the mask indicate whether the corresponding bit of the file permission should be set off, (set to zero). By default, virtually all UNIX commands attempt to set all reasonable permission bits to one when
creating the file. A command that creates a data file (such as a text file), tries to create the file with permissions of 666. In octal, this would grant read and write permission to you the file's owner, to other members of your UNIX group, and to all
other system users; it would however leave the execute permission unset. Commands which create executable files (such as cc and ld) attempt to set the file's permissions to 777, which in octal would set the read, write, and execute bits for all users.
Because of this default action by UNIX commands, it is the function of the file creation mask to specify permissions you don't want set. When you set a bit in the file creation mask, it causes the corresponding bit of the file's permissions to be forced
to zero. Bits not set in the file creation mask are interpreted as don't care: the file permission bit stays unchanged.
Now, the bits of the file permissions, from left to write, are written rwxrwxrwx, where the first three bits represent read, write, and execute permissions for the file's owner; the second set of three bits represent read, write, and execute permissions
for the file's group; and the third set of three bits are the permissions for other users. To grant read and write permissions to the file's owner, but only read access to other users, the appropriate file permissions setting would be the bits 110100100.
Writing this in octal, you arrive at the familiar permissions value of 644, which you may already have seen in the output of the ls command.
Remember that UNIX commands try to create files with all reasonable permissions set. For a data file, these bits are 110110110, corresponding to rw-rw-rw-. To get the permissions switched to rw-rr, you need to set off the fifth and eight
bits. A file creation mask of 000010010 (in octal 022) would do the trick. When the file is created, UNIX lines up the bits in the file permissions requested by the command, and your file creation mask, like this:
1 1 0 1 1 0 1 1 0 attempted file permissions 0 0 0 0 1 0 0 1 0 file creation mask 1 1 0 1 0 0 1 0 0 actual file permissions
What you have to do when using the umask command, therefore, is first to decide what file permissions you would like assigned to your new files by default, and then write a bit mask as an octal number which sets the appropriate file permission bits to
zero.
As it happens, most UNIX users want to reserve write permission for their files to themselves, but are willing to let other people look at the files. The appropriate file creation mask for this is 022 in octal. In many cases, the system administrator
sets up the system so that the umask 022 command is automatically executed for you when you login in. If the administrator has not set up a default, or you wish to use another file creation mask, you can set a new mask in your login profile.
The actual syntax of the umask command is straightforward:
To display the current process file creation mask, use the umask command as follows:
% umask 022
You can also use umask to set the process file creation mask by specifying the octal argument as follows:
% umask octal
The process file creation mask is set to the bit pattern corresponding to the low-order nine bits of the octal number octal.
C shell provides two commands for echoing arguments to standard output: echo and glob. The only difference between them is the delimiter used to separate words in the output line.
The echo command, though most often used when writing shell scripts, also comes in handy in a number of keyboard situations, for example when constructing a pipe to a non-interactive command. One of the best examples of the echo command is using it to
display the value of a shell variable:
% echo $path /usr/bin /usr/ucb/bin /usr/local/bin /home/jjv/bin %
In this case, it is the variable substitution expression $path which does the real work; the echo command provides only the step of printing the value on the terminal. Nonetheless, without the echo command it would be cumbersome to check the value of a
variable: the set command will also print variable variables, but it prints all variables, sometimes producing a lengthy list that takes time to search for the entry you want.
The glob command, on the other hand is rarely used in any context. It was originally intended to be called from a C program (not a shell script), to get the shell to expand a filename wildcard expression. However, most C programmers don't use this
technique because it relies on the existence of the C shell.
The echo command prints a line containing its arguments to standard output. The syntax for the command is as follows:
echo [ -n ] wordlist
The arguments are printed with one intervening blank between them and a newline character after the last one. The echo command does not modify the words in wordlist in any way, but the arguments as seen by echo might differ from those on the original
command because of variable, command, and history replacement and filename globbing. For example, the following command
echo Directory $dir contains these files: *.c
might generate the following line to standard output:
Directory /usr/lib1 contains these files: myprog.c bigprog.c
Specify option -n to suppress printing a newline character; this allows the next input or output to occur on the same line as the output of the echo command.
The glob command also prints a line containing its arguments to standard output. The syntax for the command is as follows:
glob [ wordlist ]
Use glob to print the words in wordlist to standard output. The words are printed with a null character between each (not white space as echo does). The last word is followed by a newline character.
The words in wordlist are subject to variable, command, and history substitution and filename expansion in the usual manner. After scanning for substitutions, the resulting strings are redivided into words, which are then written using the null
character delimiter.
The glob command is similar to echo, differing only in the delimiter used to separate words in the output line. Because most terminals cannot print a null character, glob is not normally used to generate terminal output. It is intended to be called from
a C language program, in the form /bin/csh -c 'glob ...', to invoke the shell substitution and filename expansion mechanisms.
Use eval to rescan the arguments arg for variable, command, and history substitutions, filename expansion and quote removal, and then execute the resulting words as a command.
eval arg ...
With eval, you can essentially write shell script lines with a shell script and execute the resulting generated commands. Remember, however, that to embed variable symbols in a string, you must hide the leading dollar sign from earlier shell
substitutions.
The eval command implemented by the C shell is equivalent to the Bourne shell eval command.
The newgrp command is the same as the UNIX newgrp command:
newgrp groupname
When issued from your login shell (not to be confused with a login shell script, the login shell is simply that shell started up for you automatically when you log in), newgrp causes the current shell to be replaced by a new shell with the real and
effective group IDs both changed to the specified group groupname. Because the shell is replaced, all context, including exported variables and aliases, is lost.
Use the newgrp command when you have been authorized by the system administrator for membership in two or more user groups, and you wish to change your group identification from your current or login group to another group. Your group identification is
used by the system when determining whether to grant you access to files.
Use time with no argument to display the amount of CPU time in seconds used by the current shell and all commands and subshells invoked since its start. This form of the command is usually of interest only to folks who are being billed for the amount of
machine time they use, as might be the case if you are renting time on a commercial machine. By entering the command with no arguments occasionally, you can monitor how much machine time you have used and limit your online time accordingly.
time [ command ]
Only for your login shell will this be the amount of machine time used since you logged- in. Also, note that this is not elapsed wall clock timeit is only machine time used.
Use the form time command to execute command and report the amount of CPU time used by the command's execution. The command must be a simple command, not a compound command, statement group, or parenthesized statement, and cannot be a pipeline.
You might be interested in timing the execution of a command if you are a production operations manager and you want to find out how much time a new application is adding to your daily workload. A development programmer would use the time command to
determine whether a new program has a performance problem. The average interactive user, however, would have infrequent occasion to use the time command.
One of the handier features of the C shell is the alias feature. An alias is a shorthand method of referring to a command or a part of a command. For example, if you have several favorite options that you always supply to the ls command, rather than
having to type the whole command every time, you can create a two-character alias. Then you can type the two-character alias, and the shell will execute its definition.
An alias can represent not only a command name, but also leading options and arguments of the command line. Any words you type following the alias name are considered to follow options and arguments included in the alias definition, allowing you to
customize the command with key options and arguments.
More complex processing can be achieved using shell scripts, where the function performed by the shell script file's name used as a command can be arbitrarily complex. The command alias feature was provided only for use as a keyboard shortcut, and
anything that can be achieved using an alias can be done with shell scripts.
You should add command aliases that you use often to your .login file, so that the alias will be defined every time you log in. It is often handy, however, to define command aliases at the keyboard for special commands you'll be using during this
session. Unless you incorporate the alias into your .login file, it will be lost when you log out.
The alias command allows you to list currently defined aliases, to define a new command alias, or to change an existing alias. The command format is
alias [ name [ definition ... ]]
For name, choose a word consisting of upper- and lowercase letters and digits. For definition, write any sequence of words that defines the command string you want name to stand for. For example, the following defines two aliases for the ls command,
each providing a different set of options. It's shorter to type the alias name for the particular style of ls command output than it is to type the ls command and options.
alias lx /usr/bin/ls -FC alias ll /usr/bin/ls -l
If you want to change the definition of an alias, you simply define it again.
Once you have defined aliases, you can display a list of their names and definitions by entering the alias command without arguments, as in the following example:
% alias alias lx /usr/bin/ls -FC alias ll /usr/bin/ls -l
You can also display the definition of a specific alias by specifying its name as an argument:
% alias lx alias lx /usr/bin/ls -FC
Alias substitution occurs early in the shell's processing cycle for commands, thereby allowing you to use globbing (filename replacement), variable substitution, command substitution, and command history substitution in the wordlist. Because of this,
you will often need to quote at least one of the words of definition, and perhaps the entire alias definition. Some people always enclose the alias definition in quotes to avoid surprises. Consider the following alias:
alias lc ls *.[ch]
For a C language programmer, the alias would be rather natural: by simply typing lc, you get a listing of all source program files in the current directory, devoid of any other file clutter.
However, the preceding alias definition will not work as expected. The filename pattern *.[ch] will be substituted on the alias command itself, and the actual alias stored (depending on the actual directory contents when you enter the alias command)
will be as you see here:
% alias lc ls app.h io.c main.c prog.c sundry.h
Because the filename pattern was replaced before the alias definition was stored by the shell, the lc alias won't list all files ending in .c or .h; it will attempt to list the files app.h, io.c, main.c, prog.c, and sundry.h whether they exist in the
current directory or not.
The alias should have been defined as follows:
% alias lc ls '*.c'
An alias definition can also use command aliases. During alias substitution, the alias definition is scanned repeatedly until no further substitutions can be made. An alias definition for name, however, cannot invoke the name alias within itself; a
reference to name in the wordlist will be taken as a reference to the shell built-in command or executable file named name, not as a reference to the alias. This allows you to use an alias to redefine a system command or shell built-in command, for
example:
% alias pg pg -cns -p"Page %d:"
You can refer to arguments of the original command line, before any substitutions were made, using the command history substitution syntax (see the section later in this chapter titled "Command History"). For example, the command
alias print 'pr \!* | lp'
defines an alias named print that executes the pr command using all the arguments of the original command line (\!*), then pipes the output to lp for printing.
To properly understand and use the alias command, you must be clear about the way an alias is used. When you define an alias by entering the alias command, the only thing that happens at that time is that the system stores the alias in computer memory.
Later, when you enter a command with the same name as the alias, the C shell does a little magic. The command you typed will not be executed in the form you typed it. Rather, the command name (which is an alias name) will be replaced by the value of the
alias. The result is a new command text, the front part of which is the alias definition, and which ends with any other arguments you typed.
For example, suppose you define an alias for the ls command as follows:
% alias lax ls -ax
If at some later time you enter this command:
% lax big*.txt
the command actually executed will be:
ls -ax big*.txt
The command alias (lax) is replaced by its definition (ls -ax). Remaining arguments on the command line (big*.txt) are simply tacked on after the alias substitution, to yield the command the computer will actually execute.
Using history substitutions in an alias provides additional flexibility, namely by allowing the executed command to employ arguments in a different order or a different form than entered, but require a little extra work from the shell. Consider the
following alias definition:
alias lsp 'ls \!* | lp'
Entering the command lsp *.c *.sh will result in alias substitution for lsp. The symbol !* will cause the arguments you entered on the line *.c *.sh to be inserted into the alias definition, rather than tacked on after. In other words, if an alias
definition contains a history substitution, the shell suspends its normal action of tacking on command arguments after the alias value. The command actually executed will be ls *.c *.sh | lp. Without this special mechanism, the executed command would have
been ls *.c *.sh | lp *.c *.sh, with the final *.c *.sh being tacked on in the usual manner, leading to an undesirable result: instead of printing a directory listing, the lp command would print the full contents of the files.
When writing an alias, you therefore need to visualize what will happen when the alias is substituted in later commands.
Use unalias to delete one or more aliases. You can delete a specific alias by specifying its name as an argument, or you can delete multiple aliases by using pattern-matching:
unalias name unalias pattern
If you specify a specific alias name, only that alias definition is deleted. If you specify a pattern, all those currently defined aliases whose names match the pattern are deleted. pattern can contain the pattern-matching characters *, ?, and [...]. In
the following example, the first line deletes the lx alias, and the second line deletes all currently defined aliases:
unalias lx unalias *
The C shell supports a number of command-line options to support special uses of the shell. These options are shown in Table 13.3.
Option |
Usage |
|
-c |
The shell executes the commands in the first argument string, then exits. Called from a C language program in the form csh -c "commands" to execute a shell command or list of commands (separated with semicolons or newline characters). |
|
-e |
If set, causes immediate termination of the shell if a command returns a nonzero exit code. This option is mainly used in shell scripts to abandon processing if a command sequence fails; it is simpler to use than individually checking the exit code of each command. |
|
-f |
If set, suppresses reading of the .cshrc initialization script. Use this option to speed up shell initialization and shell script execution. (See "Customizing Your Shell Environment" later in this chapter for more information about the .cshrc file.) |
|
-i |
Forces the shell to use interactive mode, even if its input is not a terminal. In interactive mode, the shell writes prompts to the standard error file prior to reading each command and ignores the Intr and Quit signals. The -i option is assumed when the shell is started with terminal input and output. |
|
-n |
If set, suppresses execution of commands. Command interpretation still occurs. Use the -n option to discover whether the shell script contains any syntax errors without actually executing commands it may contain. |
|
-s |
If set, prevents interpretation of the first command-line argument of csh as a shell script filename. Used when you are executing a stream of commands from standard input and you wish to set one or more argv arguments on the shell command linefor example, csh -s /usr/bin < file. Command-line arguments can be referenced by the commands in file. |
|
-t |
Forces the shell to terminate after reading and executing one line from standard input. If the command must be continued onto more lines, append \ to all lines but the last. The shell does not buffer up input when this option is set; it can therefore be used to read and execute the next line from a currently open file. |
|
-v |
Sets the verbose variable. The verbose variable causes the shell to echo commands to the terminal before any substitutions are made and before the commands' execution. Sometimes used to assist with debugging a shell script, in the form csh -v filename. |
|
-x |
Sets the echo variable. Commands are echoed to the terminal after substitution and filename generation but before execution. Sometimes used to assist with debugging a shell script, in the form csh -x filename. |
|
-V |
Like -v but sets the verbose option before processing the .cshrc initialization script. Use this option to display lines from the .cshrc script as they are executed. |
|
-X |
Like -x but sets the echo option before processing the .cshrc initialization script. |
Unless one of the -c, -i, -s, or -t options is set, the shell construes the first command-line argument as the name of a file to be executed. Remaining command-line arguments are assigned to the $1, $2, _ variables, and to the argv array variable. The
-c option allows only one command-line argument and takes it as a list of commands to be executed; after execution of the argument string, csh exits. When the -i, -s, or -t option is set, the shell assigns all arguments including the first to the $1,
$2,_variables and the argv array variable.
The shell supports additional options that you can switch on or off during shell operation. These options are controlled by variables; if the variable is set, the corresponding option is activated; if it is not, the option is off. These options are
described in the section titled "Using Predefined Variables" later in this chapter. Briefly, their names are echo, ignoreeof, noclobber, noglob, nonomatch, notify, and verbose.
Additionally, the shell variables cdpath, history, mail, path, prompt, and shell, although not options as such, allow you to control certain shell behaviors such as searching for commands and checking for mail. See the section titled "Using
Predefined Variables" for further information.
The shell's command history service maintains a list of previously executed commands. You can use command history for two purposes: as a reference to determine what you've already done, and, with history substitution, as a shorthand method to reuse all
or part of a previous command in entering a new command.
The history command enables you to print all or selected lines of the current command history.
history [ -r ] [ n ]
To display all the lines currently held in the history list, enter the history command (it takes no arguments):
% history 1 cd src 2 ls 3 vi foo.c 4 cc foo.c 5 grep '#include' foo.c
The shell displays each line preceded with a line number. You can use the line number to refer to commands with the history substitution mechanism. Line numbers start with 1 at the beginning of your session.
The amount of history a shell maintains is dependent on the amount of memory available to the shell. History is not saved in an external disk file, so capacity is somewhat limited. You can set the history variable to a value indicating the number of
lines of history you want the shell to maintain; it will keep that number of lines and more if possible, but your specification is only advisory. The value of history must be a simple number to be effective. For example, set history=25 retains at least
twenty-five lines of history.
To limit the number of lines displayed, specify an integer decimal for n to limit the number of lines displayed to the last n lines of history.
Specify the -r option to print history lines in reverse order, from the most recent to the oldest.
History substitutions are introduced into a command with the ! (exclamation point, sometimes called the bang operator). You append one or more characters to ! to define the particular kind of history substitution you want. If followed by a blank, tab,
newline, equal sign (=), or open parenthesis (, the exclamation point is treated as an ordinary character.
You can write a history substitution anywhere in the current shell input line, as part or all of the command. When you enter a command containing one or more history substitutions, the shell echoes the command after performing the substitutions so that
you can see the command that will actually be executed. (You do not have an opportunity to correct the command; it is executed immediately after being displayed.)
The simplest forms of history substitution are !! and !number. The !! symbol is replaced with the entire previous command line. The expression !number is replaced with line number from the command history list.
Suppose command history currently contains the following lines:
% history 1 cd src 2 ls 3 vi foo.c 4 cc foo.c 5 grep '#include' foo.c
If you now enter the command !!, the shell will repeat the grep command in its entirety. Press return to execute the grep command, or type additional words to add to the end of the grep command:
% !! sna.h grep '#include' foo.c sna.h
Continuing the example, suppose after running grep you want to edit the foo.c file again. You could type the vi command as usual, but it already appears in command history as line 3. A history substitution provides a handy shortcut:
% !3 vi foo.c
That's almost all there is to basic history substitution. Actually, the shell supports any of the following forms for referring to command history lines:
|
|
|
|
|
|
|
|
|
|
You can do more with history substitutions than merely reuse a previous command. The shell also provides extensions to the history operator that allow you to select individual words or a group of words from a history line, inserting the selected word or
words into the current command. These extensions are in the form of a suffix beginning with : (colon). For example, !vi:1 is replaced not with the most recent vi command, but rather with its first argument word. Similarly, !3:3-4 is replaced with arguments
3 and 4 of history line 3. You can use any of the following expressions as word selectors by appending the expression to a line reference, preceded with a colon:
|
0 |
First word of the command (usually the command name). |
|
n |
nth argument of the command. Arguments are numbered from 1. Note that 0 refers to the command name, which is actually the first word of the line, whereas 1 refers to the second word of the line. |
|
^ |
Same as :1, the first argument. |
|
$ |
Last argument word of the command. |
|
% |
For the !?string? format, the word matched by string. Use this word selector only with the !?string? history reference. Its value is the entire word matching string, even though string might have matched only a part of the word. |
|
m-n |
Multiple word substitution. Replaced with words m through n of the history line. For m and n, specify an integer number, or one of the special symbols ^, $, or %. |
|
m- |
Substitutes words beginning with the mth word and extending up to but not including the last word. |
|
-n |
Same as 0-n; substitutes words beginning with the first word of the history line (the command name) through the nth word. |
|
m* |
Same as m-$; substitutes words beginning with the mth word and extending through the last word of the line. |
|
* |
Same as ^-$; substitutes all argument words of the line. |
If the word selector expression you want to write begins with ^, $, *, -, or %, you can omit the colon between the line selector and the word selector. For example, !vi* refers to all the arguments of the previous vi command, and is the same as !vi:* or
!vi:^-$.
You can use any number of word selectors in the same command line. By combining multiple word selectors, you can reuse arguments of a previous command in a different order and use arguments originally appearing on different commands. For example, the
command rm !115^ !117^ removes files that were named on two earlier commands.
When counting words of a previous command line, the shell takes quoting into consideration but uses the line as it appears in the history list; words generated by variable or command substitution or filename generation are not accessible.
You can append modifiers to a word selector to alter the form of the word before insertion in the new command. A modifier is written in the form :x, where x is a letter specifying how the word should be modified. For example, !vi^:t will
substitute the tail of the first argument of the vi command: for the argument /usr/X/lib/samples/xclock.c, the value of :t will be xclock.c.
The following modifiers can be appended to a word selector to alter the selected word before substitution:
|
:h |
Removes a trailing path component. Successive :h modifiers remove path components one at a time, right to left. Thus for the argument /usr/X/lib/samples/xclock.c, :h will return /usr/X/lib/samples, whereas :h:h will return /usr/X/lib. |
|
:r |
Removes a filename suffix of the form .string. For example, for the argument foo.c, :r will return foo. |
|
:e |
Removes all but the filename suffix. For the argument foo.sh, :e will return .sh. |
|
:t |
Removes all leading components of a path, returning just the filename part. For the word /usr/bin/ls, the value of :t is ls. |
|
:s/x/y/ |
Replaces the string x in the selected word with the string y. String x cannot be a regular expression. The symbol & appearing in y is replaced with the search string x, thus :s/bill/&et/ will substitute billet for bill. Any character can be used in place of the slash, for example :s?/usr?/user?. The final / can be omitted if followed by a newline. The delimiter (/ or your delimiter) or & can be used as a text character by escaping it with \ (backslash), for example :s/\/usr/\/user/. The search string x can be omitted, in which case the search string of the previous :s on the same line is used, or if no previous :s occurred, the string of !?string? is used. |
|
:& |
Reuses the previous string substitution modifier :s appearing in the same command line, thus !grep:2:s/bill/marty/ !:3:& is the same as !grep:2:s/bill/marty/ !3:s/bill/marty/. |
|
:p |
Used in any history substitution expression on the command line, causes the shell to print the command after substitutions, but not to execute it. Use :p to try the effect of a history substitution before executing it. |
|
:q |
Encloses the substituted word or words in quotes to prevent further substitutions. |
|
:x |
Breaks the selected word or words at blanks, tabs, and newlines. |
Normally, a modifier affects only the first selected word. When selecting multiple words, such as with !12:2*, you can apply a modifier to all of the selected words by inserting a g in front of the modifier letter. For example, !12:2*:gh will apply the
:h modifier to all of the words. The g is not valid with the :p, :q, and :x modifiers.
You can omit the command identifier from a history substitution when using two or more ! expressions in the same line; successive history references then refer to the same command as the first. For example,
% vi %grep^:t %:3:t %:4:t
all refer to the same grep command but select the first, third, and fourth arguments.
The history mechanism supports a special abbreviation ^ useful for correcting a keying error in the previous line. The general form of the abbreviation is ^x^y, where x and y are strings. The previous command line is selected and searched
for string x; if found, it is replaced with y, then executed. For example, after the command cd /usr/ban, enter the line ^ban^bin (or ^an^in) to execute the command as cd /usr/bin. The caret ^ must be the first nonblank character of the line to be
recognized as a line editing substitution. This abbreviation is available only for the immediately preceding command line; you must use the full history expression !line:s/x/y/ to edit any line other than the last.
One final, important provision of the history substitution mechanism is that you can enclose any history reference in braces {} to isolate it from characters following it. Thus, !{vi^:h}.c forms a word beginning with the selected history reference and
ending in .c.
You can use shell variables to hold temporary values, and shell scripts can use variables to manage changeable information. The shell itself also has variables of its own that you can use to customize features of the shell and your shell environment.
A variable is actually an area of the shell's memory set aside to hold a string of characters and given a name. You assign the name of a variable when you define it with set. You can change the value of a variable in several ways.
The shell provides a complex set of syntax for referring to the value of a variable. Any variable reference, when scanned in a command line, is replaced by the corresponding value of the reference before the command is executed. In its simplest form, a
variable reference simply replaces the name of a variable with its string value.
This section looks at the kinds of variables the shell supports and the rules for naming them and referring to their value.
The shell imposes no set limit on the size of variable names. People commonly use variable names of six to eight characters, and names up to sixteen characters are not unusual.
A variable name can consist of only uppercase and lowercase letters and digits. The name cannot begin with a digit, because names beginning with a digit are reserved for use by the shell. General usage indicates the use of all capital letters for the
names of environment variables, and all lowercase letters for local variables, although the shell imposes no such restriction.
You assign a value to a variable using the set or setenv built-in commands, depending on the type of variable you are setting.
Use the set statement to create new local variables and optionally to assign a value to them. Local variables are known only to the current shell and are not passed to shell scripts or invoked commands.
Use the setenv statement to create new environment variables. Environment variables are passed to shell scripts and invoked commands, which can reference the variables without first defining them (no setenv statement is required or should be used in a
shell script for passed environment variables you wish to access). (See the section "Displaying and Setting Global Environment Variables" below for more about environment variables.)
A shell variable can contain any characters, including unprintable characters, as part of its value. A shell variable can also have a null value, which is a zero-length string containing no characters. A variable with a null value differs from an unset
variable: a reference to the former has the effect of merely deleting the variable reference, because it is replaced with a zero-length string; a reference to an unset variable is an error, generates an error message, and causes the shell interpretation of
commands to stop.
The set command can be used to display or set local variables.
set set name=word set name=(wordlist) set name[index]=word
Use set with no arguments to list the currently defined variables and their respective values. The listing includes exported variables as well as local variables.
Any of the operand formats can be combined on a single set statement; each assigns a value to a single shell variable or element of an array variable. Note that no white space should separate the variable name, equal sign, or value when writing an
assignment; any white space appearing in word or wordlist must be hidden with quotes.
Use set name to define a variable name and to initialize it with a null string. This form can be used to set a number of shell options (such as set ignoreeof). A variable with a null value is not the same as an unset variable; the former exists
but has no value, whereas the latter does not exist. A reference to an unset variable results in a shell error message; a reference to a null variable results in substitution of the null string.
Use set name=word to assign the string word as the current value of variable name. The string replaces the current value of name if the variable is already defined; otherwise, a new variable named name is created. If word contains
characters special to the shell (including blanks or tabs), it must be enclosed in single or double quotes.
Use the form set name=(wordlist) to assign each word in wordlist to successive elements of the array variable name. After the assignment, the expression $name[1] refers to the first word in wordlist, $name[2] to the second
word, and so on. Any word in wordlist must be quoted if it contains characters special to the shell (including blanks or tabs).
Use the form set name[i]=word to assign the string word as the current value of the ith element of the array variable name. For i, specify a decimal integer number not less than 1. Note that you do not have to assign a value to
every element of an array. The number of elements in an array is effectively the highest-numbered element to which a value has been assigned. Elements to which no value has been assigned have an effective value of the null (zero-length) string. Also note
that you cannot assign a (wordlist) to an array element; an array variable can have multiple values, but each element can represent only one string value.
Use the unset command to delete one or more shell variables from the shell's memory.
unset pattern
The unset command is effective for variables defined with the set command only; use the unsetenv command to delete variables defined with setenv.
For pattern, specify a string that might optionally contain one or more occurrences of the pattern-matching characters *, ?, or [...]. All local variables known to the shell whose names match the specified pattern are deleted. You will receive no
warning message if nothing matches pattern, and no confirmation documenting the variables that were deleted.
Use the setenv statement to create new environment variables. Environment variables are passed to shell scripts and invoked commands, which can reference the variables without first defining them (no setenv statement is required or should be used in a
shell script for passed environment variables you wish to access). See the section later in this chapter titled "Customizing Your Shell Environment" for more about environment variables.
The format of the setenv command is: setenv [name=value ...]. Issued without arguments, the setenv command lists all global environment variables currently in effect, together with their values. Used in the form setenv
name=value , the shell creates a new global variable with the specified name and assigns the string value as its initial value. If the value contains contains characters such as the space or tab, be sure to enclose the value string in
quotes. (See the section "Quoting and Escaping Special Characters" in this chapter for information about shell special characters and the use of quoting techniques.)
UNIX also provides a command (env) for displaying the current list of environment variables and their values. The env command actually supports a number of options and arguments for modifying the current environment.
The section "Using Predefined Variables" below provides a list of all variables (local and environment) which are defined by the C shell. Environment variables defined by other UNIX components are defined in the documentation for those
components. Unfortunately there is no comprehensive list of environment variables, because some are defined by non-shell programs. The mailx command, for example, defines some variables, and the vi command looks for some variables of its own. Altogether
the environment variable pool is optional anyway: if you don't know of a variable some UNIX command uses, the command will still work without it. At any rate, be aware that the shell is not responsible for defining all environment variables; it merely
provides a means for manipulating and accessing them.
To delete global environment variables, you use the unsetenv command:
unsetenv variablename unsetenv pattern
Use the unsetenv command to delete one or more environment variables from the shell's memory. The unsetenv command is effective only for variables defined with the setenv command; use the unset command to delete variables defined with set.
To delete a particular variable definition, specify its name as variablename. To delete multiple variable definitions, use pattern to specify a string that might optionally contain one or more occurrences of the pattern-matching characters
*, ?, or [...]. All environment variables known to the shell whose names match the specified pattern are deleted. You will receive no warning message if nothing matches pattern, and no confirmation documenting the variables that were deleted.
You obtain the value of a shell variable by writing a variable reference on the command line. A variable reference results in replacement of the entire reference expression, including the $ that introduces the reference, the variable's name, and any
other characters that might adorn the reference, with a string value of the reference.
A variable reference does not itself define the start or end of a word: the reference can be a complete word or a part of a word. If a part of a word, the substituted string is combined with other characters in the word to yield the substituted word.
However, if the reference value substitutes one or more blanks or tabs into the word, the word will be split into two or more words unless it is quoted. For example, if the value of var is "two words," then the reference expression $var will
appear as two words after substitution, but the quoted string "$var" will appear as the one word "two words" afterward.
A variable reference can result in the substitution of the value of either a local or a global variable: a local variable is used if it exists, otherwise the value of an environment variable is taken. Remember, a variable reference refers to a variable
by name: local and environment variables cannot have the same name, so a reference is to whatever variable has the specified name.
You can use any of the variab