What's GNU

This month's column concludes the article on Plan 9 From Bell Labs, and those parts of it that have been re-implemented in freely available software.

Last month we described the origins of Plan 9, the sam editor, and the 9term terminal emulator. Well, what about the shell to run inside the window? Here too, the Plan 9 authors took the opportunity to rethink the issue of just how should a shell work. The Plan 9 shell is called rc, because it “runs commands”.

The rc Shell

Although in many ways the Bourne shell is a simple, elegant, high-level programming language, it has a serious flaw, in that it was designed to be much like a macro-processing language. Input text is scanned, rescanned, and rescanned again, as each stage of processing is performed. (This is carried to an almost absurd length in the Korn shell, with something like eleven different processing stages.) This leads to rather complicated and baroque quoting rules, with the need for nested escape sequences.

In rc, the input text is scanned and parsed exactly once. The language has a real yacc-based grammar, making it clear what everything means. The quoting rules are very simple. Quoted text must be enclosed between single quotes. To get a single quote inside a quoted string, double it (as in FORTRAN). An explicit operator is used to provide string concatenation, and variables can be lists of strings, not just single strings.

The syntax is closer to that of C or awk, instead of Bourne's Algol 68. This leads to less clutter, avoiding unnecessary keywords and semi-colons. It is much more like C than the fabled csh is.

rc provides shell functions, and signal handlers are written as functions with special names (sighup, sigterm, etc.), instead of using strings. I/O redirection is also more powerful, with a notation for hooking up file descriptors besides 0 and 1 to the input and output ends of a pipe.

A freely distributable clone of rc is available. It was written by Byron Rakitzis, and implements the language described in the rc paper, with some extensions. The beauty of rc is that it is small and fast, and shell programs can be quite elegant. It also runs on just about any kind of Unix system.

When using rc with 9term, it is conventional to set the primary prompt to be just a single semi-colon, and the secondary prompt to be empty. This allows you to snarf entire commands, including the prompt, and resend them. The semi-colon is treated as a simple null statement. The use of double-clicking to select the whole line, and the default saved action of the menus make sending and resending the same line over again extremely simple; most of the work can be done with just the mouse.

The Resources sidebar lists the ftp location of the rc shell. There is also a mailing list of people who use rc.

The es Shell

es is the “extensible shell”. Paul Haahr and Byron Rakitzis thought it would be interesting to try and combine some of the capabilities of functional languages with those of Unix shells. Many internal capabilities of the shell (such as I/O redirection and setting up pipelines) are available as built-in functions in the language, and program fragments can be passed around as arguments to functions.

es provides first class functions, lexical scope, an exception system, and rich return values (i.e. functions can return values other than just numbers). Most of this is beyond the scope of this article to explain. es is described in a paper in the Winter 1993 Usenix Conference Proceedings. It helps to read this paper, and also to go through the archives of the mailing list to see how the language evolved. For the full details on es, you'll need to read the paper, the man page, and the file initial.es in the es distribution. It is a good idea to also look at the sample .esrc file, too.

Basically, the idea behind es is to take the primitive operations that a shell does, such as forking processes, creating pipes, and setting up I/O redirections, and make them available as functions that a user program can call directly. In turn, traditional shell syntax is built on top of these primitive operations.

Lexical scoping allows you to save the definition of an operation, and then replace it with your own operation on top of the previous one. Here is an example from the paper on es. This code implements a pipeline profiler. It saves the definition of %pipe, which creates pipes, and provides a new one that times each component of the pipeline, using the old %pipe to actually create the pipeline. (es is the prompt from es used for examples in the paper. The default prompt is a semi-colon.)

es > let (pipe = $fn-%pipe) {
                fn %pipe first out in rest {
                        if (~ $#out 0) {
                                time first
                        } {
                                $pipe { time $first } $out
$in { %pipe $rest }
es> cat paper9  tr -cs a-zA-Z0-9 '\012' | sort |
 uniq -c | sort | -nr  sed 6q
213 the
150 a
120 to
115 of
109 is
 96 and
 2r    0.3u   0.2s      cat paper9
 2r    0.3u   0.2s      tr -cs a-zA-Z0-9 \012
 2r    0.5u   0.2s      sort
 2r    0.4u   0.2s      uniq -c
 3r    0.2u   0.1s      sed 6q
 3r    0.6u   0.2s      sort -nr

This is a simple example, yet it illustrates some of the power available in es. es really deserves a column on its own. For more information, see the above sources and the mailing list archive.

The sidebar lists the ftp location for es, and a mailing list is also available.