C shell


The C shell is a Unix shell created by Bill Joy while he was a graduate student at University of California, Berkeley in the late 1970s. It has been widely distributed, beginning with the 2BSD release of the Berkeley Software Distribution which Joy first distributed in 1978. Other early contributors to the ideas or the code were Michael Ubell, Eric Allman, Mike O'Brien and Jim Kulp.
The C shell is a command processor typically run in a text window, allowing the user to type commands. The C shell can also read commands from a file, called a script. Like all Unix shells, it supports filename wildcarding, piping, here documents, command substitution, variables and control structures for condition-testing and iteration. What differentiated the C shell from others, especially in the 1980s, were its interactive features and overall style. Its new features made it easier and faster to use. The overall style of the language looked more like C and was seen as more readable.
On many systems, such as macOS and Red Hat Linux, csh is actually tcsh, an improved version of csh. Often one of the two files is either a hard link or a symbolic link to the other, so that either name refers to the same improved version of the C shell.
On Debian and some derivatives, there are two different packages: csh and tcsh. The former is based on the original BSD version of csh and the latter is the improved tcsh.
tcsh added filename and command completion and command line editing concepts borrowed from the Tenex system, which is the source of the "t". Because it only added functionality and did not change what was there, tcsh remained backward compatible with the original C shell. Though it started as a side branch from the original source tree Joy had created, tcsh is now the main branch for ongoing development. tcsh is very stable but new releases continue to appear roughly once a year, consisting mostly of minor bug fixes.

Design objectives and features

The main design objectives for the C shell were that it should look more like the C programming language and that it should be better for interactive use.

More like C

The Unix system had been written almost exclusively in C, so the C shell's first objective was a command language that was more stylistically consistent with the rest of the system. The keywords, the use of parentheses, and the C shell's built-in expression grammar and support for arrays were all strongly influenced by C.
By today's standards, C shell may not seem particularly more C-like than many other popular scripting languages. But through the 80s and 90s, the difference was seen as striking, particularly when compared to Bourne shell, the then-dominant shell written by Stephen Bourne at Bell Labs. This example illustrates the C shell's more conventional expression operators and syntax.
The Bourne sh lacked an expression grammar. The square bracketed condition had to be evaluated by the slower means of running the external test program. sh's if command took its argument words as a new command to be run as a child process. If the child exited with a zero return code, sh would look for a then clause and run that nested block. Otherwise, it would run the else. Hard-linking the test program as both "test" and "" gave the notational advantage of the square brackets and the appearance that the functionality of test was part of the sh language. sh's use of a reversed keyword to mark the end of a control block was a style borrowed from [ALGOL 68.
By contrast, csh could evaluate the expression directly, which made it faster. It also claimed better readability: Its expressions used a grammar and a set of operators mostly copied from C, none of its keywords were reversed and the overall style was also more like C.
Here is a second example, comparing scripts that calculate the first 10 powers of 2.
Again because of the lack of an expression grammar, the sh script uses command substitution and the expr command.
Finally, here is a third example, showing the differing styles for a switch statement.
In the sh script, ";;" marks the end of each case because sh disallows null statements otherwise.

Improvements for interactive use

The second objective was that the C shell should be better for interactive use. It introduced numerous new features that made it easier, faster and more friendly to use by typing commands at a terminal. Users could get things done with a lot fewer keystrokes and it ran faster. The most significant of these new features were the history and editing mechanisms, aliases, directory stacks, tilde notation, cdpath, job control, and path hashing. These new features proved very popular, and many of them have since been copied by other Unix shells.

History

Editing operators

Aliases

Directory stack

Tilde notation

Filename completion

Cdpath

Job control

Path hashing

Overview of the language

The C shell operates one line at a time. Each line is tokenized into a set of words separated by spaces or other characters with special meaning, including parentheses, piping and input/output redirection operators, semicolons, and ampersands.

Basic statements

A basic statement is one that simply runs a command. The first word is taken as name of the command to be run and may be either an internal command, e.g., echo, or an external command. The rest of the words are passed as arguments to the command.
At the basic statement level, here are some of the features of the grammar:

Wildcarding

I/O redirection

Joining

Piping

Variable substitution

Quoting and escaping

Command substitution

Background execution

Subshells

Control structures

The C shell provides control structures for both condition-testing and iteration. The condition-testing control structures are the if and switch statements. The iteration control structures are the while, foreach and repeat statements.

if statement

There are two forms of the if statement. The short form is typed on a single line but can specify only a single command if the expression is true.
if command
The long form uses then, else and endif keywords to allow for blocks of commands to be nested inside the condition.
if then
commands
else if then
commands
...
else
commands
endif
If the else and if keywords appear on the same line, csh chains, rather than nests them; the block is terminated with a single endif.

switch statement

The switch statement compares a string against a list of patterns, which may contain wildcard characters. If nothing matches, the default action, if there is one, is taken.
switch
case pattern1:
commands
breaksw
case pattern2:
commands
breaksw
...
default:
commands
breaksw
endsw

while statement

The while statement evaluates an expression. If it is true, the shell runs the nested commands and then repeats for as long as the expression remains true.
while
commands
end

foreach statement

The foreach statement takes a list of values, usually a list of filenames produced by wildcarding, and then for each, sets the loop variable to that value and runs the nested commands.
foreach loop-variable
commands
end

repeat statement

The repeat statement repeats a single command an integral number of times.
repeat integer command

Variables

The C shell implements both shell and environment variables. Environment variables, created using the setenv statement, are always simple strings, passed to any child processes, which retrieve these variables via the exec #envp|envp argument to main.
Shell variables, created using the set or @ statements, are internal to C shell. They are not passed to child processes. Shell variables can be either simple strings or arrays of strings. Some of the shell variables are predefined and used to control various internal C shell options, e.g., what should happen if a wildcard fails to match anything.
In current versions of csh, strings can be of arbitrary length, well into millions of characters.

Expressions

The C shell implements a 32-bit integer expression grammar with operators borrowed from C but with a few additional operators for string comparisons and filesystem tests, e.g., testing for the existence of a file. Operators must be separated by whitespace from their operands. Variables are referenced as $name.
Operator precedence is also borrowed from C, but with different operator associativity rules to resolve the ambiguity of what comes first in a sequence of equal precedence operators. In C, the associativity is left-to-right for most operators; in C shell, it is right-to-left. For example,
The parentheses in the C shell example are to avoid having the bit-shifting operators confused as I/O redirection operators. In either language, parentheses can always be used to explicitly specify the desired order of evaluation, even if only for clarity.

Criticism

Although Stephen Bourne himself acknowledged that csh was superior to his shell for interactive use, it has never been as popular for scripting. Initially, and through the 1980s, csh could not be guaranteed to be present on all Unix systems, but sh could, which made it a better choice for any scripts that might have to run on other machines. By the mid-1990s, csh was widely available, but the use of csh for scripting faced new criticism by the POSIX committee, which specified that there should only be one preferred shell, the Korn Shell, for both interactive and scripting purposes. The C shell also faced criticism from others over the C shell’s alleged defects in syntax, missing features, and poor implementation.
Syntax defects were generally simple but unnecessary inconsistencies in the definition of the language. For example, the set, setenv and alias commands all did basically the same thing, namely, associate a name with a string or set of words. But all three had slight but unnecessary differences. An equal sign was required for a set but not for setenv or alias; parentheses were required around a word list for a set but not for setenv or alias, etc. Similarly, the if, switch and looping constructs use needlessly different keywords to terminate the nested blocks.
Missing features most commonly cited are the lack of ability to manipulate the stdio file handles independently and support for functions. Whereas Bourne shell functions lacked only local variables, Csh’s aliases – the closest analogue in Csh to functions – were restricted to single lines of code, even though most flow control constructs required newlines to be recognized. As a result, Csh scripts could not be functionally broken down as C programs themselves could be, and larger projects tended to shift to either Bourne shell scripting or C code.
The implementation, which used an ad hoc parser, has drawn the most serious criticism. By the early 1970s, compiler technology was sufficiently mature that most new language implementations used either a top-down or bottom-up parser capable of recognizing a fully recursive grammar. It is not known why an ad hoc design was chosen instead for the C shell. It may be simply that, as Joy put it in an interview in 2009, “When I started doing this stuff with Unix, I wasn’t a very good programmer.” The ad hoc design meant that the C shell language was not fully recursive. There was a limit to how complex a command it could handle.
It worked for most interactively typed commands, but for the more complex commands a user might write in a script, it could easily fail, producing only a cryptic error message or an unwelcome result. For example, the C shell could not support piping between control structures. Attempting to pipe the output of a foreach command into grep simply didn't work.
Another example is the unwelcome behavior in the following fragments. Both of these appear to mean, “If ‘myfile’ does not exist, create it by writing ‘mytext’ into it.” But the version on the right always creates an empty file because the C shell’s order of evaluation is to look for and evaluate I/O redirection operators on each command line as it reads it, before examining the rest of the line to see whether it contains a control structure.
The implementation is also criticized for its notoriously poor error messages, e.g., “0 event not found”, which yields no useful information about the problem.

Influence

The C shell was extremely successful in introducing a large number of innovations including the history mechanism, aliases, tilde notation, interactive filename completion, an expression grammar built into the shell, and more, that have since been copied by other Unix shells. But in contrast to sh, which has spawned a large number of independently-developed clones, including ksh and bash, only two csh clones are known.
In 1986, Allen Holub wrote On Command: Writing a Unix-Like Shell for MS-DOS, a book describing a program he had written called "SH" but which in fact copied the language design and features of csh, not sh. Companion diskettes containing full source for SH and for a basic set of Unix-like utilities were available for $25 and $30, respectively, from the publisher. The control structures, expression grammar, history mechanism and other features in Holub's SH were identical to those of the C shell.
In 1988, Hamilton Laboratories began shipping Hamilton C shell for OS/2. It included both a csh clone and a set of Unix-like utilities. In 1992, Hamilton C shell was released for Windows NT. The Windows version continues to be actively supported but the OS/2 version was discontinued in 2003. An early 1990 quick reference described the intent as "full compliance with the entire C shell language " but with improvements to the language design and adaptation to the differences between Unix and a PC. The most important improvement was a top-down parser that allowed control structures to be nested or piped, something the original C shell could not support, given its ad hoc parser. Hamilton also added new language features including built-in and user-defined procedures, block-structured local variables and floating point arithmetic. Adaptation to a PC included support for the filename and other conventions on a PC and the use of threads instead of forks to achieve parallelism, e.g., in setting up a pipeline.