The C-Shell

The C-Shell

One of the first “new” shells to emerge was the csh or C-Shell. It is so named because much of the syntax it uses is very similar to the C programming language. This isn’t to say that this shell is only for C programmers, or programmers in general. Rather, knowing C makes learning the syntax much easier. However, it isn’t essential. (Note: The csh syntax is similar to C, so don’t get your dander up if it’s not exactly the same.)

The csh is normally the shell that users get on many UNIX systems. Every place I ever got a UNIX account, it was automatically assumed that I wanted csh as my shell. When I first started out with UNIX, that was true. In fact, this is true for most users. Because they don’t know any other shells, the csh is a good place to start. You might actually have tcsh on your system, but the principles are the same as for csh.

As you login with csh as your shell, the system first looks in the global file /etc/cshrc. Here, the system administrator can define variables or actions that should be taken by every csh user. Next, the system reads two files in your home directory: .login and .cshrc. The .login file normally contains the variables you want to set and the actions you want to occur each time you log in.

In both of these files, setting variables have a syntax that is unique to the csh. This is one major difference between the csh and other shells. It is also a reason why it is not a good idea to give root csh as its default shell. The syntax for csh is

set variable_name=value

whereas for the other two, it is simply

variable=value

Because many of the system commands are Bourne scripts, executing them with csh ends up giving you a lot of syntax errors.

Once the system has processed your .login file, your .cshrc is processed. The .cshrc contains things that you want executed or configured every time you start a csh. At first, I wasn’t clear with this concept. If you are logging in with the csh, don’t you want to start a csh? Well, yes. However, the reverse is not true. Every time I start a csh, I don’t want the system to behave as if I were logging in.

Let’s take a look as this for a minute. One of the variables that gets set for you is the SHELL variable. This is the shell you use anytime you do a shell escape from a program. A shell escape is starting a shell as a subprocess of a program. An example of a program that allows a shell escape is vi.

When you do a shell escape, the system starts a shell as a new (child) process of whatever program you are running at the time. As we talked about earlier, once this shell exits, you are back to the original program. Because there is no default, the variable must be set to a shell. If the variable is set to something else, you end up with an error message like the following from vi:

invalid SHELL value: <something_else>

where <something_else> is whatever your SHELL variable is defined as.

If you are running csh and your SHELL variable is set to /bin/csh, every time you do a shell escape, the shell you get is csh. If you have a .cshrc file in your home directory, not only is this started when you log in, but anytime you start a new csh. This can be useful if you want to access personal aliases from inside of subshells.

One advantage that the csh offered over the Bourne Shell is its ability to repeat, and even edit, previous commands. Newer shells also have this ability, but the mechanism is slightly different. Commands are stored in a shell “history list,” which, by default, contains the last 20 commands. This is normally defined in your .cshrc file, or you can define them from the command line. The command set

history=100

would change the size of your history list to 100. However, keep in mind that everything you type at the command line is saved in the history file. Even if you mistype something, the shell tosses it into the history file.

What good is the history file? Well, the first thing is that by simply typing “history” with nothing else you get to see the contents of your history file. That way, if you can’t remember the exact syntax of a command you typed five minutes ago, you can check your history file.

This is a nice trick, but it goes far beyond that. Each time you issue a command from the csh prompt, the system increments an internal counter that tells the shell how many commands have been input up to that point. By default, the csh often has the prompt set to be a number followed by a %. That number is the current command, which you can use to repeat those previous commands. This is done with an exclamation mark (!), followed by the command number as it appears in the shell history.

For example, if the last part of your shell history looked like this:

21 date 22 vi letter.john 23 ps 24 who

You could edit letter.john again by simply typing in !22. This repeats the command vi letter.john and adds this command to your history file. After you finish editing the file, this portion of the history file would look like

21 date 22 vi letter.john 23 ps 24 who 25 vi letter.john

Another neat trick that’s built into this history mechanism is the ability to repeat commands without using the numbers. If you know that sometime within your history you edited a file using vi, you could edit it again by simply typing !vi. This searches backward though the history file until it finds the last time you used vi. If there were no other commands since the last time you used vi, you could also Enter !v.

To redo the last command you entered, you could do so simply by typing in !!.

This history mechanism can also be used to edit previously issued commands. Lets say that instead of typing vi letter.john, we had typed in vi letter.jonh. Maybe we know someone named jonh, but that’s not who we meant to address this letter to. So, rather than typing in the whole command, we can edit it. The command we would issue would be !!:s/nh/hn/.

At first, this seems a little confusing. The first part, however, should be clear. The “!!” tells the system to repeat the previous command. The colon (:) tells the shell to expect some editing commands. The “s/nh/hn/” says to substitute for pattern nh the hn. (If you are familiar with vi or sed, you understand this. If not, we get into this syntax in the section on regular expressions and metacharacters.)

What would happen if we had edited a letter to john, done some other work and decided we wanted to edit a letter to chris instead. We could simply type !22:s/john/chris/. Granted, this is actually more keystrokes than if we had typed everything over again. However, you hopefully see the potential for this. Check out the csh man-page for many different tricks for editing previous commands.

In the default .cshrc are two aliases that I found quite useful. These are pushd and popd. These aliases are used to maintain a directory “stack”. When you run pushd <dir_name>, your current directory is pushed onto (added to) the stack and you change the directory to <dir_name>. When you use popd, it pops (removes) the top of the directory stack and you change directories to it.

Like other kinds of stacks, this directory stack can be several layers deep. For example, lets say that we are currently in our home directory. A “pushd /bin” makes our current directory /bin with our home directory the top of the stack. A “pushd /etc” brings us to /etc. We do it one more time with pushd /usr/bin, and now we are in /usr/bin. The directory /usr/bin is now the top of the stack.

If we run popd (no argument), /usr/bin is popped from the stack and /etc is our new directory. Another popd, and /bin is popped, and we are now in /bin. One more pop brings me back to the home directory. (In all honesty, I have never used this to do anything more than to switch directories, then jump back to where I was. Even that is a neat trick.)

There is another useful trick built into the csh for changing directories. This is the concept of a directory path. Like the execution search path, the directory path is a set of values that are searched for matches. Rather than searching for commands to execute, the directory path is searched for directories to change into.

The way this works is by setting the cdpath variable. This is done like any other variable in csh. For example, if, as system administrator, we wanted to check up on the various spool directories, we could define cdpath like this:

set cdpath = /usr/spool

Then, we could enter

cd lp

If the shell can’t find a subdirectory named lp, it looks in the cdpath variable. Because it is defined as /usr/spool and there is a /usr/spool/lp directory, we jump into /usr/spool/lp. From there, if we type

cd mail

we jump to /usr/spool/mail. We can also set this to be several directories, like this:

set cdpath = ( /usr/spool /usr/lib /etc )

In doing so, each of the three named directories will be searched.

The csh can also make guesses about where you might want to change directories. This is accomplished through the cdspell variable. This is a Boolean variable (true/false) that is set simply by typing

set cdspell

When set, the cdspell variable tells the csh that it should try to guess what is really meant when we misspell a directory name. For example, if we typed

cd /sur/bin (instead of /usr/bin)

the cdspell mechanism attempts to figure out what the correct spelling is. You are then prompted with the name that it guessed as being correct. By typing in anything other than “n” or “N,” you are changing into this directory. There are limitations, however. Once it finds what it thinks is a match, it doesn’t search any further.

For example, we have three directories, “a,” “b,” and “c.” If we type “cd d,” any of the three could be the one we want. The shell will make a guess and choose one, which may or may not be correct.

Note that you may not have the C-Shell on your system. Instead, you might have something called tcsh. The primary difference is that tcsh does command line completion and command line editing.