The shell is something used so often that it gets taken for granted by advanced users; that familiarity frustrates and confuses the beginner. The innards of a shell may seem as interesting as the physics that make your desk chair feel comfortable. The value in this addition to your brain cache is a stronger ability to solve user problems.
What the shell?
Shells have proliferated to meet the demands of Unix users. The original, no-frills Bourne shell gives you a functional command line interpreter and access to multitasking through background processes. Add in C language syntax, job control, a history mechanism, and filename completion, and you get the C shell. System V brings the Korn shell, with Bourne shell-like built-in operators, more elaborate history and aliasing mechanisms, and a line-editing feature that speaks both vi and emacs.
All shells employ the notion of a login shell, nominally the shell you entered courtesy of /bin/login. But there's no simple way to determine if your shell was the first spawned, so the first character of the command name is used as an indicator. If it's a minus sign, the shell is assumed to be a login shell. Login makes the distinction by changing /bin/csh to -csh in the command line passed to exec(). You'll see these tweaked command names in the output of ps.
Bourne and Korn shells get going by reading the global /etc/profile, followed by the user's .profile. Korn shells complete setup by looking for the file named by the variable ENV, defaulting to .env, executing it if found. All Bourne and Korn shell initialization occurs for login shells only. Create common user environments in /etc/profile, but be sure all of your users employ the same shell to avoid unpleasant surprises.
The C shell consumes the user's .cshrc followed by the .login file in a login shell. Confusion can result after using .login to unintentionally re-initialize variables correctly set in .cshrc. If the user doesn't have a .login file, a common /etc/.login file is read. Your .cshrc is always read, even for non-login shells, which contributes to the start-up time of the C shell.
Getting to /bin/yes
Every line you type gets passed to the shell for inspection, substitution, and execution. Before invoking the command you so dutifully typed, the shell has many duties of its own:
The last character in a command line is an ampersand (&) if you want the job to run in the background. The Bourne shell lets you start background jobs, but gives you no way to move between them once started. The C and Korn shells implement job control so you can suspend, resume, and move jobs between foreground and background status.
The interaction between keyboard and shell subsumes the tty driver and its configuration (known as a line discipline), signal handling, interaction with the window system, and command line editing. Normally, each character you type at the keyboard is passed to the tty driver, only making it to the shell when you hit Return. Cut and paste between windows is a special case; it looks like input typed at the keyboard, but it's the window system and not your fingers that supplies the bits. Character echo is a function of the tty driver, as are higher-level functions like character erase, word erase, and line kill. To wipe out an entire input line in a fit of frustration, use control-U (line kill). Binding of line discipline functions to control keys is handled by thestty command, which also lets you remap the suspend, interrupt (SIGINTR), and quit (SIGQUIT) signal-generation keys.
Goodies like stty, echo, or read commands belong in the .login file. They'll have bizarre effects on the rcp, rsh, and rdump (rmt) commands if they appear in your .cshrc file. When the Berkeley r commands are used, they spawn an instance of/bin/csh on the remote machine, which executes your .cshrc file. Any unexpected output from the shell start-up confuses the local utility, resulting in some very cryptic Unix error messages.
The Korn shell (and the tcsh shell) adds another level of input with line editing. Enable this with a set -o vi or set -o emacs in your .profile. You start in vi input mode, so what you type becomes shell input. Hit Escape, and you're in command-editing mode where most of your favorite editor sequences work.
The C shell offers filename completion. When enabled (set filec in your .cshrc file), pressing Escape expands a unique prefix into a full filename.
The C shell history mechanism is another re-execution mechanism, with sed-like commands replacing the in-line editing of the Korn shell. The history lives in the shell's memory history; depth is set by the variable history. If not set, no history is maintained. savehist sets how many commands are stored in $HOME/.history upon logout, used to seed the history stack on your next login.
Korn shell history is entirely file-based. Recently executed commands go into $HOME/.sh_history, or the filename specified by $HISTFILE. When multiple shells share one history file, their commands are intermixed. Separate the history for each shell, putting a line of the form HISTFILE=$HOME/.sh_history$$ in your .profile. Be sure to clean up old history files before logging out.
The ksh built-in fc calls an editor (named in the FCEDIT variable) to modify commands from the history file. Giving - as an editor name reduces the edit-execute cycle to a replace-and-execute sequence similar to the C shell. For example, fc -e - lotus in ksh is equivalent to !lotus in csh; fc -e - is usually aliased to a single character, such as r.
There are numerous shell environment issues worth mention. Here are some additional pointers and caveats:
noisy >& /dev/null # csh
noisy > /dev/null 2>&1 # sh, ksh
In the Bourne and Korn versions of the line, the 2>&1 construct means "duplicate file descriptor 2 from file descriptor 1," or send stderr to stdout. Since stdout was already redirected to /dev/null, this sends all output into the bit bucket. Redirection arguments are order-sensitive; if you put the stderr and stdout redirection first, stderr will go to whatever stdout was, and only stdout will go to /dev/null.
source /usr/local/config/common.csh # csh
. /usr/local/config/common.sh # sh, ksh
Executing the script as a command applies the changes to a subshell, which promptly exits at the end of the script, leaving your login shell none the wiser.
We'll cover variable substitution, evaluation, execution, and other mechanisms that make the shell intriguing and powerful next month. Explore your shell environment; you'll find it full of shortcuts and tools that make you bolder with sleights of hand and keyboard.
Check out the sidebars Big shells, no perl and Models and options.
About the author
Hal Stern is an area systems engineer with Sun Microsystems in the Northeast, where he specializes in networking, performance tuning, and kernel hacking. He can be reached at email@example.com.
You can buy Hal Stern's Managing NFS and NIS at Amazon.com Books.
(A list of Hal Stern's Sysadmin columns in SunWorld Online.)
Models and options
Bourne C Korn ---------------------------------------------------------------------------- Job control no yes yes History no yes, login "memory" in yes, with editing per $HOME/.history shell in $HISTFILE, can combine shell histories in one file Global setup /etc/profile /etc/.login if none /etc/profile exists for user Line editing no filename completion, vi or emacs style more in tcsh File descriptor by number stdin, stdout, or by number and via redirection stdout/stderr process substitution
If you have problems with this magazine, contact firstname.lastname@example.org
Last updated 1 March 1995.