Revision [29]
This is an old revision of Philosophy made by DavidLee on 2007-11-26 10:22:37.
Philosophy
In the opinion of the Author, the Unix "shell" (Wikipedia) is one of the most elegant, practical and powerful command and scripting languages ever written. It has stood up to 30 years of use and abuse, and while it has been copied, cloned, rewritten, duplicated, "improved" and otherwise mimicked in many variants of unix and other operating systems, the core design, functionality, and syntax has not improved significantly, nor needed to be improved. Those who grew up using "modern" Windowing systems or primitive CLI's (Command Line Interpreter) (Wikipedia) may not fully appreciate the elegance and functionality of the unix shell (originally the Bourne shell, /bin/sh) and its variations.
For this discussion the term "Unix Shell" refers to any of the unix shells derived from the Bourne Shell (/bin/sh) although in particular the Bourne Shell as it embodies the earliest and cleanest design. Some offshoots such as the "C Shell" (csh), in an attempt to add features, loose such significant amounts of the original design philosophy. Other languages, such as perl, which were derived, or atleast inspired from, the Unix Shell, are so different that I consider them in their own category as full fledge programming languges.
Unix Shell Design
It is beyond the scope of this document to fully describe in detail the design of the Unix Shell. The following is a rough overview of the major design points which make the Unix Shell elegant, unique and powerful as they implement the philosophy of design. It is these design points and philosophy which xmlsh attempts humbly to borrow from the unix shell. An important consideration and distinguishing factor of a "shell" as apposed to a CLI or a programming language is that a Shell is designed to optimize 2 very different use cases simultaneously. This is a very difficult thing to do and usually fragments computer languages into different categories.For this discussion, I make a distinction between a shell and a login shell.
A quick "litmus test" of a login shell vs some other kind of programming language is would you configure that program to be your 'login shell' on a unix system (the first program that runs when you login). I know many people boast from time to time (myself included) how their favorite program de'jour is so powerful it could be a shell (common examples being perl and emacs) but beyond the occasional playful experimentation, this never happens.
On the other hand a shell may have a bit lower bar if its designed for a subset of tasks, and may not be designed for, or appropreate for everything.
In any case, all shells have the following use cases in mind.
* Command Line Interpreter
A shell must function as a Commmand Line Interpreter. That is, it must funciton well as a primary interface to humans operating a computer using text (keyboard). Early shells ran on "Line Printers" and Teletypes (TTY), and were later modified to work well with "Terminals" (dubbed "Glass TTY's"). Humans type in via a keyboard a "command" to execute and the CLI interprets that command and invokes the necessary process. Dedicated CLI's typically perform this job very well, implementing various sorts of line oriented editing features and simplifications to allow a short command to execute any pre existing process (program) on the computer.
* Scripting
A shell shouild also function as a scripting language. That is, often the process one wants to describe to a compuler is more complex then can be described by typing in a line or 2 without mistakes. CLI's often impliment scripting by allowing multiple lines (identical to what you would have typed in) to be stored in a file and the file executed with one command. This is certianly useful, but it is a tiny step. Usually flow control (if/while/goto), variables, and other more exotic features are needed or desired in order to describe a process. There are many shells and CLI's that manage both of these use cases, but very rarely are both achieved equally well. One hurdle is that typcially the CLI and Scripting cases are not entirely interoperable. That is, you can have commands in a "script" which behave differently when run interactively, or simply are not supported interactively. This makes writing scripts much more difficult then necessary because you cant manually "try out" something intended for a script on the command line. Sometimes this is intential as the shell's authors design philosophy is that scripts are inherantly different then interactive use. Other times it is due to fundimental limitations or design of the operating system. Since shells typically have evolved as the first layer above the OS (and in fact thats why the term "shell" is used), they often reflect the design philosophy of the operating system itself more then their own design.
The Unix Shell adds some additional fundimental design philosophy which is key to its elegance and power.
* The TTY is a file.
In the Unix Shell, the "console" or the "TTY" is considered identical to a file. That is, everything you can do in a script you can do interactively, and visa-versa. This is fundimentally a reflection of the design of the Unix operating system where the console is of the same "type of thing" as a file and for the most part is indistinguishable - they are both a "file" and use the same core system calls to access. While there are a few things you can do to a disk file that you cant do to a tty, and visa versa, both the OS and the Unix Shell attempt to hide these inconsistancies as much as possoble. The end result is that prety much anything you can put into a "script" you can type interactively and it will have exactly the same effect. This merging of the "Scripting" and "CLI" use cases is extremely powerful, and makes authoring, debugging and using scripts a much more simple and natural process then on systems where scripts and CLI behave differently.
* Tools and Pipes
The single most unique and powerful feature of the Unix shell is the concept of tools. All shells prior to the Unix Shell, and most shells since are based on Monolithic Processes.That is, a single process (program, or executable) is designed to do a complete job all by itself. The purposes of both the CLI and scripts is to setup the parameters for a monolithic process, such as its input and output files, and options, Execute the Big Process then clean up. Sometimes very large tasks created by stringing together multiple monolithic processes to do many things in order, for example, format a document then send it to a printer. But no matter how many monolithic processes you string together in series, the complexity of what can be done is linear. That is, nothing new is created out of the combonation of processes. At best a long tedious sequential task is turned into a single command (script), but its still fundimentally a sequence of monolithic processes that had to be pre-created (the program created prior) and they can do nothing more then was intended by the original programmer. The Unix Shell is designed differently at a very fundimental level. While certianly there are monolithic programs that do only one thing, a core philosophy is that you can have small programs that do very little, but that can interoperate with other programs seamlessly to create new programs. I like to think of this as "Lego". Using the Toy analogy, the Monolithic model is like a having a toybox which has fully complete toys in it, like a Car, an Airplane, a stuffed bear. These toys were pre-built by the toy builders to do only one thing and do it well. The tools and pipes model is like having legos where each piece is both specialized and universal. With Lego you can build your own car or airplane, or space ship or anything else, not limited to what was pre-built.
To implement this design, the core set of unix tools (and future designed tools are encouraged to be written similarly) are specifically built to interop. Like legos, they naturally have fitting 'connectors'. Again, this is a natural reflection of the underlying operating system (Unix). There are 3 types of unnamed connection points. On startup a process is given the "command line" as an array of strings. At runtime have access to 3 universally implemented streams on startup (stdin, stdout, stderr). On exit, a process exits with an integer exit code. In addition, processes have access to named connection points (namely files).
The Unix Shell elegantly manages these connection points in simple and expressive ways. Inputs streams can be connected to outputs via pipes (|). Output streams can be connected to command line arguments. Exit values can be connected to command line arguments. With the philosophy of small interconnected tools, even flow control was originally implemented as seperate command processes (although later optimized as builtin commands). Furthermore, hierarchially, groupings of operations can be connected to atomic or other groups, indefinately. This connectivity allows both linear and hierarchical construction and hence the complexity (how many different things you can do) with a set of primatives is no longer linear, it is exponential.