Monthly Columns
 

Shells - A Rather Confusing Topic?

Copyright © 1998 Konrad Heuer

Introduction

The command-line shell remains the most important user interface to any UNIX system. Unfortunately, users tend to get confused by the different syntax and behaviour of the various shells widespread in the UNIX world. The goal of this article is to give some recommendations to help a user decide on a shell. Furthermore, it will demonstrate how to make the desired shell to be the login shell and how to influence the behaviour of the shell by its startup files. Finally, some simple but useful hints will be given to make life easier with the command line.

Overview

From the beginning, the UNIX system developers decided to put the user interface, called shell, into the user space. Therefore a lot of different shells evolved. It's not too hard for a C programmer to write a shell if they can spend the time to do so. Of course, today there is no need to write a new shell from scratch. Here is a brief look at some of the more important shells:
 
Binary Name Short Description
sh Bourne-Shell Grandfather/mother of all shells; still heavily used to write scripts especially for system startup and shutdown.
csh C-Shell Standard Shell of Berkeley UNIX since 3BSD; first shell with command history, C-style syntax, hardly used for scripts because of some lacks and bugs.
ksh Korn-Shell Standard Shell  of UNIX System V; compatible with Bourne-Shell, first shell providing command-line editing.
bash Boune-Again-Shell Modern successor of Bourne-Shell; compatible with the latter but not with Korn-Shell; some C-Shell features included, command-line editing, applicable for programming scripts.
tcsh T-C-Shell Modern successor of C-Shell; compatible with the latter except for its bugs, still somewhat lacking in respect to programming; modern command-line editing, splendid for interactive work.
zsh Z-Shell The nearly almighty shell; incorporating a lot features of the already mentioned shells, but seems to be not as popular as Bourne-Again- or T-C-Shell.

Which Shell should be Used?

Each *BSD system comes with sh and csh. The first one is used by the many system scripts like /etc/rc, the second one is the default login shell of root. The pure Korn-Shell  is available only in System V but there's a free clone called  the Public Domain ksh. The other shells mentioned above are freely distributed according to the `usual' license policies in the free software market. The package collections provide these shells ready to run. So, which shells are recommended?

The command line interface is still alive on UNIX systems, and many people like it. Nevertheless it is very important for the shell to make the user feel comfortable with the command line. Both state of the art and popular are bash and tcsh. They come ready to use with vi or emacs style command line editing and name completion. No doubt, different people have different preferences depending on the personal background. A good compromise seems to be to select tcsh for interactive use and bash for shell programming. Why? In tcsh, name completion rules can individually and largely be defined if the user wishes to do so, and tcsh offers a spelling correction. On the other hand, as mentioned above, tcsh still lacks some features important for programming, while bash does not.

The Road Map

For the rest of this article we will take a closer look at bash and tcsh. As mentioned above, installation is made easy by the ports or packages collections. But installation, of course, is only one task that has to be done, and other steps will have to be taken.

Therefore in the following sections it will be explained

  • how to make the desired shell to be the login shell,
  • how to set up some (minimal) startup files,
  • and how to make use of some nice features of bash and tcsh.
If you really don't know bash and tcsh you may want to read the last section first!

How to Change the Login Shell?

The login shell spawned automatically by the login program after a successful login is set in the password file individually for each user by the last entry of the corresponding line. So, either root can set the login shell with vipw, or the user does it by himself/herself with the chpass or the chsh command. The system administrator must allow a specific shell to be login shell by adding its complete path name to /etc/shells.

The Shell Startup Files

There are different classes of shell startup files. First, there are files read only by a login shell and files read by each new shell after it has been invoked. Second, there are system-wide startup files and user-specific startup files.

After a tcsh has been invoked as a login shell, it will read the system-wide startup files (in this order) /etc/csh.cshrc and /etc/csh.login. Then it will read, if found in the user's home directory, .tcshrc (or as a fallback, .cshrc) and .login.  Non-login shells only read /etc/csh.cshrc and .tcshrc or .cshrc.  Non-login shells are typically invoked by a shell script or a shell escape from an application.

bash as a login shell reads /etc/profile and, from the user's home directory, .bash_profile or, as fallbacks, .bash_login or .profile. A non-login bash only reads a startup file if the environment variable ENV is set to the name of the file.

The term `read' used above is meant in the sense of reading the file and executing the statements found. Often this behaviour of shells is called `sourcing' the file.

Before looking at some examples a note seems to appropriate: It's not possible to explain all the stuff  found in the examples in detail. The extent of this article would increase beyond limits. If you don't understand, please take a look at the man pages which will explain the shell instructions and variables used below.

A typical example for /etc/profile might be:

# System-wide profile for sh(1), bash(1) and ksh(1)

if [ "$USER" = root ]; then
  PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin
  PS1="`hostname -s`# "
else
  PATH=/bin:/usr/bin:/usr/X11R6/bin:/usr/local/bin:/usr/games:.
  PS1="`hostname -s`"$ "
fi

ENV=/etc/bashrc
PRINTER=hpdj

export ENV PATH PRINTER PS1

. $ENV

This file defines a standard search path and a standard shell prompt for all users. A root login should be treated specially to make the path and the prompt differ. Environment variables like HOME and USER are set by the system before /etc/profile gets sourced. Additionally, PRINTER is set to the name of a printer queue of this system which shall be the default printer queue. Furthermore, please note that the system administrator here takes the chance to define (by setting ENV) a system wide startup file for non-login shells (bash) which could look like:

# System-wide bashrc file for bash(1)

alias lf='ls -F'
alias ll='ls -l'

HISTSIZE=200
HISTFILESIZE=200

if [ -f $HOME/.bashrc ]; then
  . $HOME/.bashrc
fi

Here for the user's convenience two alias commands are defined, the number of commands  to keep in the history list and in the history file are set, and a user-specific bash startup file is sourced if it exists.

Next we take a look at the corresponding startup files for tcsh.  The file /etc/csh.login might be:

# System-wide login file for csh(1) and tcsh(1)

if ( "$user" == root ) then
  set path = ( /sbin /usr/sbin /bin /usr/bin /usr/X11R6/bin /usr/local/bin )
else
  set path = ( /bin /usr/bin /usr/X11R6/bin /usr/local/bin /usr/games . )
endif

setenv PRINTER hpdj

The shell variable path is automatically kept equivalent to the environment variable PATH by tcsh. The shell prompt should  be set in /etc/csh.cshrc since the corresponding shell variable is not mapped to an environment variable:

# System-wide cshrc file for csh(1) and tcsh(1)

if ( $?prompt ) then

  alias lf ls -F
  alias ll ls -l

  set history = 200
  set savehist = 200

  if ( "$user" == root ) then
    set prompt = "`hostname -s`# "
  else
    set prompt = "`hostname -s`% "
  endif

endif

Additionally, as in /etc/bashrc above, two alias commands and the number of commands to remember are defined. Because of the leading if-condition all instructions will only be executed for interactive shells and not for shells invoked by scripts.

General rules for writing startup files are:

  • Try to distinguish things that need to be set only once after a login (like an environment variable) and things that need to be set for each newly invoked shell (like an alias command or a shell variable), and put them into the right file.
  • Startup instructions (even potentially) important for all users should be placed into the system-wide files. The user-specific files are for user-specific instructions only. It's not a good idea to put important things into a prototype file and to copy that file to the user's home directories. In the worst case, if the system administrator wants to change something, he might then have to check and edit all user-specific startup files!
  • Keep the startup files small. Otherwise, on a heavily loaded system, you might get a distinct delay after a login or shell invocation.

Some Useful Hints for bash and tcsh

Only special and very useful features common to bash and tcsh will be mentioned here. Moreover, probably most people will prefer emacs mode for command line editing, and we'll concentrate on that.

Most important of course is that the up and down cursor keys can be used to step in both directions through the command history, and that the left and right cursor keys can be used to move the cursor on the command line to insert or to delete characters where changes are needed.

Furthermore, there are a lot of emacs-style keystrokes to move the cursor and to edit the line.You want to cut from the cursor to the end of line? Just press the control key, hold it down, and press `K' (this sequence is usually abbreviated as ^K). You want to insert the just cut text somewhere? Move the cursor to that position and insert the text with ^Y.

Another common necessity is to repeat the last word of the previous line. For example, you just inspected the contents of a file with

more ~/some-direcory/some-file

and now decide to remove it. Just type:

rm M-_

Well, `M-_'  is again an abbreviation for some keystrokes. It means: Press the escape key, release it, and type the underscore. Or, alternatively: Press the alternate key, hold it down, and type the underscore.

A very useful feature of bash and tcsh is name completion. Imagine the following command:

lpr -Pmyprinter a-very-long-filename

You don't kneed to type the long file name or to replace it with wildcards like `*' or `?'. Just type in the first few letters of the name and press the tab key. If the first few characters give a unique abbreviation, the shell will complete the name; if not,  it will make the system beep and complete the name as far as possible.

Imagine another situation: You know there's a command you need now. You've forgotten the name but you remember the first character(s). Type them, and with bash press the tab key twice; with tcsh, press it once and then ^D.  Both shells will display all commands in the search path beginning with the letter(s) you typed.  In the lucky situation the letter(s) you typed give a unique abbreviation of the command, pressing the tabulator key once will complete the command name.

Name completion does also work for shell and environment variables if referenced by a leading dollar sign.

There are a many more features of bash and tcsh, of course. The best way to learn about them is to read the man pages and to concentrate on those features you don't know yet, but which sound most interesting to you. As time goes on, you'll learn a lot of useful  things and you'll be noticing a remarkable speedup in what you do. Eventually, you might share the opinion that GUI tools especially for system administration are nothing but nice toys - real work can better be done by using the command line.

Konrad Heuer, kheuer@gwdg.de