* The UNIX Shell * Authors: S.R. Bourne The Unix shell has a dual personality; it acts as a programming/scripting language and also as a command language. The basics: Execute cmd and wait for it complete: cmd arg1 arg2 ... Execute cmd in the background: cmd arg1 arg2 ... & Three file descriptors are automatically associated with each process. FD 0 is the standard input (stdin), FD 1 is the standard output (stdout), and FD 2 is the standard error output (stderr). The shell can redirect these from the default terminal to a particular file (and also to devices, through the named devices): Redirect stdout to file, replacing it: cmd > file Redirect stdout to file, appending to it: cmd >> file Redirect stdin from file: cmd < file Pipeline stdout of cmd1 into stdin of cmd2: cmd1 | cmd2 For pipelines, the kernel deals with scheduling cmd1 and cmd2 such that the rates of production and consumption are balanced, i.e. the producer should not egregiously outpace the consumer, and the consumer cannot run when there is nothing to consume. Pipelines can consist of more than two processes. Pipelines make it possible to write useful filter programs that manipulate their input and then output the result. Examples: grep, sort, wc, cut. Even sed, awk, perl, etc. On the programming side, the shell has conditionals, loops, and case statements. Shell procedures (shell scripts): sh file arg1 arg2 ... The arguments are bound to $1, $2, etc. In a move that foreshadows Perl, the default list for a for loop is the list of these arguments: for x do Here, $x will take on $1, $2, ... in turn. Lesser-known features: - Here documents - readonly variables - default variable values: ${var-default} - mandatory variables: ${var?message} - matching character subsets: [a-z] - eval - trap: trap signal_no Implementation of commands, redirection, pipelines: Lots of the work is really done by the unix kernel, which provides facilities that make it easy to implement much of the shell. Simple execution uses fork and exec (pseudocode): while(1) cmd = get_command(); if (fork() == parent) wait(); else exec(cmd); For background tasks, the wait is not done, instead the interpreter loop in the parent immediately continues. Redirection relies on changing stdin/stdout/stderr by closing and then reopening (bug? We now have freopen()) after the fork is performed but before the exec; the child process will have its output/input redirected. Pipes are used to implement pipelines; the pipe is created before the fork, and the processes forked off can use the same file handle, one to read and the other to write.