Jim Mohr's SCO Companion
Copyright 1996-1998 by James Mohr. All rights reserved. Used by permission of
Be sure to visit Jim's great Linux Tutorial web site at http://www.linux-tutorial.info/
Shells and Basic Utilities
Most UNIX users are familiar with "the shell." This is
where you input commands and get output on your screen. Often, the
only contact users have with the shell is logging in and immediately
starting some application. Some administrators, however, have
modified the system to the point where users may never even see the
shell or in extreme cases have eliminated the shell completely for
If you never get to the point where you can actually input commands
and see their output, then this chapter may be a waste of time. If
your only interaction with the operating system is logging in, then
most of this entire book can only serve to satisfy your curiosity.
However, if you are like most users, understanding the basic workings
of the shell will do wonders to improve your ability to use the
system to it's fullest extent.
Up to this point, we have referred to the shell as an abstract
entity. In fact, that's the way it is often referred to as there are
many different shells that you can use. Each has it's own
characteristics (or even quirks), but all behave in the same general
fashion. Since the basic concepts are the same, I will avoid talking
about specific shells until later.
In this chapter, we are going to cover the basic aspects of the
shell. We'll talk about issue commands and how they system responds.
Along with that, we'll cover how commands can be made to interact
with each other to provide you with the ability to make your own
commands. We'll also talk about the different kinds of shells, what
each has to offer and some details of that particular shell behaves.
Talking to SCO UNIX: The Shell
As I mentioned in chapter 1, the shell is essentially the users'
interface to the operating system. The shell is a Command Line
Interpreter. Through it, you issue commands that are interpreted by
the system and certain actions are carried out. Often the state where
the system is sitting at a prompt, waiting for you to type input is
referred to (among other things) as being at the shell prompt or at
the command line.
For many years before the invention of graphic user interfaces such
at X-Windows, the only way to input commands to the operating system
was through a command line interpreter, or shell. In fact, shells
themselves were thought of as wondrous things during the early days
of computers as prior to them, users had no direct way to interact
with the operating system.
Most shells, be it under DOS, UNIX, VMS, or other operating systems,
have the same input characteristics. In order to get the operating
system to do anything, you have to give it a command. Some commands,
such as the date command under UNIX, do not require anything else to
get them to work. If you type in date
and press return, that's what appears on your screen: the date.
(Among other things)
Some commands need something else to get them to work. This is called
an argument. Some commands, like mkdir
(Used to create directories) can work with only one argument. As in
Others, like cp (to copy
files) require multiple arguments. As in cp
In many cases, you can pass flags to commands to change their
behavior. These flags are generally referred to as options.
For example, if you wanted to create a series of sub-directories
without creating every one individually you could run mkdir
with the -p option like
mkdir -p one/two/three/four.
In principle, anything added to the command line after the command
itself is an argument to that command. Using the terminology above,
some arguments are optional and some options are required. The
convention is that an option changes the behavior whereas an argument
is acted upon by the command. Generally options are preceded by a
dash (-), whereas arguments are not. I've said it before and I will
say it again, nothing is certain when it comes to UNIX. By realizing
that these two terms are often interchanged, you won't get confused
when you come across one or the other. I will continue to use option
to reflect something that changes the command's behavior and argument
to indicate something that is acted upon.
Each program or utility has its own set of arguments and options, so
you will have to look at the manual pages or man-pages for the
individual commands. You can call this up from the command line by
is the name of the command you want information about. Also, if you
are not sure what the command is, OpenServer has the whatis
command that will give you a brief description of that command.
Many commands require that the option appear immediately after the
command and before any arguments. Others have options and arguments
interspersed. Again, look at the man-page for the specifics of a
If you need a quick reminder what a command does, you can use the
whatis command. This gives
you a one line description of the command. For more details see the
Often times you just need a quick reminder as to what the available
option are and what they syntax is. Rather than going through the
hassle of calling up the man-page, a quick way is to get the command
to give you a usage message. As its name implies, a
usage message reports the usage of a particular command. I normally
use -? as the option to
force the usage message as I cannot think of a command where -?
is a valid option.
The Search Path
It may happen that you know there is a program by a particular name
on the system, but when you try to start it from the command line,
you are told that the file is not found. Since you just ran it
yesterday, you assume it has gotten removed or you really don't
remember the spelling.
The most common reason for this is that the program you want to start
is not in your search path. Your search path is a pre-defined set of
directories in which the system looks for the program you type in
from the command line (or is started by some other command). This
saves time since the system does not have to look through every
directory trying to find the program. Unfortunately, if the program
is not in one of the directories specified in your path, the system
cannot start the program unless you explicitly tell it where to look.
To do this you would have to specify either the full path of the
command or a path relative to where you are currently located.
Let's look at this issue for a minute. Think back to our discussion
of files and directories. I mentioned that every file on the system
can be referred to by a unique combination of path and file name.
This applies to executable programs as well. By inputting the
complete path, you can run any program, whether it is in you path or
Let's take a program that is in everyone's path like date.
(At least it should be) The date
program resides in the /bin
directory, so it's full path is /bin/date.
If you wanted to run it, you could type in /bin/date,
press enter and you might get something that looks like this:
Sat Jan 28 16:51:36 PST 1995
However, since date is in your search path, you need only input its
name without the path to get it to run.
You could also start a program by referencing it through a
relative path. This is simply the path in relation to your
current working directory. In order to understand the syntax of
relative paths we need to backtrack a moment. As I mentioned you can
refer to any file or directory by specifying the path to that
directory. Since they have special significance, there is a way of
referring to either your current directory or it's parent
directory. The current directory is referenced by '.' and it's
parent by '..'. (Often referred to in conversation as 'dot' and
Since directories are separated from files and other directories by a
/, a file in the current
directory could be referenced as ./file_name
and a file in the parent directory would be referenced as
../file_name. You can reference the parent of the parent by
just tacking on another ../, and
then continue on to the root directory if you want. So the file
../../file_name is in a
directory two levels up from your current directory. This slash (/)
is referred to as a forward slash as compared to a back-slash
(\) which is used in DOS to separate path components.
When interpreting your command line, shell interprets everything up
to the first / as a
directory name. Assume that we were in the root (upper-most)
directory. We could access date
in one of several ways. The first two, date
and /bin/date we already
know about. Using the fact that ./
refers to the current directory means we could also get to it like
this: ./bin/date. This is
saying relative to our current directory (./)
look in the bin
sub-directory for the command date.
If we were in the /bin
directory, could start the 'date' command like this: ./date.
This is useful when the command you want to execute is in your
current directory, but the directory is not in your path. (More on
that in a moment.)
We can also get the same results from the root directory by starting
the command like this: bin/date.
If there is a ./ at the
beginning, it knows that everything is relative to the current
directory. If all that's there is a /,
the system knows that everything is relative to the root directory.
If no slash is at the beginning, the system searches until it gets to
the end of the command or encounters a slash whichever comes first.
If there is a '/' there (like in our example) it translates this to
be a sub-directory of the current one. So executing the command
bin/date is translate the
same as ./bin/date.
Let's now assume that we were in our home directory, /usr/jimmo
(for example). We could obviously access the date
command simply as date
since it's in our path. However, to access it by a relative path, we
could say ../../bin/date.
The first ../ moves up one
level into /usr. The
second ../ moves us up
another level to /. From
there we look in the sub-directory
bin for the command date.
Keep in mind that throughout this whole process, our current
directory does not change. We are still in /usr/jimmo.
Searching your path is only done for commands. If we were to enter vi
(vi is a text editor)
file_name and there was no file called file_name
in our current directory, vi would start editing a new file. If we
had a sub-directory called text
where file_name was, we
would have to access either as vi
./text/file_name or vi
text/file_name. Of course, we could access it with the
absolute path of vi
One problem that regularly crops up for users coming from a DOS
environment is that the only place UNIX looks for commands is
in your path. However, even if not specified in your path, the first
place DOS looks is your current directory. This is not so for UNIX.
UNIX only looks in your path.
For most users this is not a problem as the current directory is
included in your path by default. Therefore, the shell will still be
able to execute something in your current directory. Root does not
have the current directory in its path. In fact, this is the way it
should be. If you want to include the current directory in root's
path, make sure it is the last entry in the path so that all "real"
commands are executed before any one a users might try to "force"
Assume a malicious user created program in his directory called more
that actually did something bad. If root were to run more
in that user's directory, what would get run is the incorrect one
with potentially disastrous results. (Note that the current directory
normally always appears at the end of the search path. So, even if
there was a program called more
in the current directory, the one in /usr/bin
would probably get executed first. However, you can see how this
could cause problems for root.)
It is common to have people working on SCO systems that have never
worked on a computer before or have only worked in pure windowing
environments like on a Macintosh. When they get to the command line
they are lost. On more than one occasion, I have talked to customers
where I have asked them to type in
cd /. There is a pause and I hear:
"Hmmm," I think to myself, "that's too many
characters." So I ask them what they typed. They respond with:
There are some conventions we need to adhere throughout this book to
in order to make things easier. One is that commands that I talk
about will be in your path unless I say otherwise. Therefore, to
access them all you need to do is input the name of the command
without the full path.
The second convention is the translation of the phrases "input
the command", "enter the command" and "type in
the command." These are translated to mean "input/enter/type
in the command and press enter." I don't know how many
times I have talked with customers and have said "type in the
command ..." and then ask them for what happens and their
response is "Oh, you want me to press enter?" Yes! Unless I
say otherwise, always press enter after inputting, entering or typing
in a command.
All this time we have been talking about finding and executing
commands, but there is one issue that I haven't mentioned. That
is the concept of permissions. In order to access a file, you need to
have permission to do so. If you want to read a file, you need to
have read permission. If you want to write to a file you need to have
write permission. If you want to execute a file you have to have
Permissions are set on a file using the chmod
command or when the file is created. The details of which I will save
for later. You can read the permissions on a file by using either the
l command or ls
-l. At the beginning of each line will be ten characters,
which can either be a dash or a letter. The first position is the
type of file, whether it is a regular file (-), a directory (d), a
block device file (b) and so on. Below are some examples of the
various file types.
- - regular file
b - block
p - named
What each of these are, we'll get into details later. If you are
curious about the format of each entry, you can look at the ls(C)
man-page for more details.
The next nine positions are broken into three groups. Each group
consists of three characters indicating the permissions. They are, in
order: read(r), write(w) and execute(x). The first group indicates
what permissions the owner of the file has. The second group
indicates the permissions for the group of that file. The last group
indicates the permissions for everyone else.
If a particular permission was not given, there will be a dash (-)
her. For example: rwx means
all three permissions have been given. In our example above, the
symbolic link /etc/custom
has read, write and execute permissions for everyone. The device
nodes /dev/tty01 and
/dev/hd00 have permissions
rw- only for the owner,
this means only read and write, but not execute permissions have been
given. The directory /bin
has read and execute permissions for everyone (r-w),
but only the owner can write to it (rwx).
Break-down of file permissions
For directories, the situation is slightly different than for regular
files. If you do not have read permission on a directory, you cannot
read the contents of that directory. Also if you do not have write
permissions on a directory, you cannot write it. This means that you
cannot create a new file in that directory. Execute permissions on a
directory mean that you can search it. That is, if even if you could
not see the contents of a particular directory, you could still
access a file there if you new it's name.
Write permission on a directory also has an interesting side effect.
Since you need to have write permissions on a directory to create a
new file, you also need to have write permissions to remove an
existing file. Even if you do not have write permissions on the file
itself, if you can write to the directory, you can erase the file.
At first this sounds odd. However, remember that a directory is
nothing more than a file in a special format. If you have write
permissions to that directory-file, you can remove the references to
other files, thereby removing them.
Before we talk about specific shells, there are some general shell
concepts we need to talk about. The first thing we should talk about
is the shell's "environment". This is all the information
that the shell will use as it runs. This includes such things as your
command search path, your logname (the name you logged in
under), even the terminal type you are using. Collectively they are
referred to as your environment variables and individually as
the "so-and-so" environment variable, such as the TERM
environment variable which contains the type of terminal you are
When you login, most of these are set for you in one way or another.
(The mechanism that sets all environment variables is shell
dependent, so we will talk about it when we get to the individual
shells.) Each can be viewed by simply typing echo
$VARIABLE. For example, if I type: echo
$LOGNAME, I get:
Typing echo $TERM, I get:
One of the most important environment variables is the PATH
variable. Remember that the PATH tells the shell where it needs to
look when determining what command it should run. One of the things
the shell does to make sense of your command is to find out exactly
what program you meant. This is done by looking for the program in
the places specified by your PATH
Although it is more accurate say that the shell looks in the
directories specified by your PATH environment variable, it is
commonly said that the shell "searches your path.” Since
this is easier to type, I am going to use that convention here.
If you were to specify a path in the command name, the shell does not
use your PATH variable to
do any searching. That is, if you issued the command bin/date,
the shell would interpret that to mean that you wanted to execute the
command date that was in
the bin sub-directory of
your current directory. If you were in /
(the root directory), all would be well and it would
effectively execute /bin/date.
If you were somewhere else, the shell might not be able to find
anything that matched.
If you do not specify any path (that is, the command does not contain
any /'s) the system will search through your path. If it finds the
command, great, if not you get a message saying the command was not
Let's take a closer look at how that works by looking at my path
variable. From the command line, if I type echo
$PATH, I get:
Ü watch the dot!!
If I type in date, the
first place that the shell looks is the
/bin directory. Since that's where date
resides, it is executed as
/bin/date. If I type in vi,
the shell looks in /bin,
doesn't find it, then looks in /usr/bin,
where it does find vi. Now
I type in getdev. ( This
is a program I wrote to translated major device numbers into the
driver name. Don't worry if you don't know what a major
number is. You will.) The shell looks in
/bin and doesn't find it. It then looks in
/usr/bin. Still not there. It then tries
/usr/dbin and /usr/ldbin
and still can't find it. When it finally gets to /usr/jimmo/bin
it finds the getdev
command and executes it. (Note that since I wrote this program, you
probably won't have it on your system.)
What happens if I had not yet copied the program into my personal bin
directory? Well, if the getdev
program is in my current directory, the shell finds a match with that
last . in my path.
(Remember that the . is
translated to the current directory so the program is executed as
./getdev.) If that final .
was missing or the getdev
program was somewhere else, the shell could not find it and would
tell me so with something like:
getdev: not found
Expressions and Metacharacters
Often the arguments that you pass to commands are file names. For
example, if you wanted to edit a file called letter,
you could enter the command vi
letter. In many cases, typing the entire name is not
necessary. Built into the shell are special characters that it will
use to expand the name. These are called metacharacters.
The two most common metacharacters are *.
The * is used to represent
any number of characters including zero. For example, if we have a
file in our current directory called letter
and we input vi let* the
shell would expand this to be vi
letter. Or if we had a file simply called let,
this would match as well.
Instead, what if we had several files called letter.chris,
letter.daniel and letter.david?
The shell would expand them all out to give me the command:
vi letter.chris letter.daniel
We could also type in vi
letter.da* which would be expanded to:
vi letter.daniel letter.david
If we only wanted to edit the letter to chris, we could type it in as
vi *chris. However, if there were two files letter.chris
and note.chris, the
command vi *chris would
have the same results as if we typed in:
vi letter.chris note.chris
In other words, no matter where the asterisk appears, the shell will
expand it to match every name it finds. If files existed in my
current directory with matching names, the shell would expand them
properly. However, if there are no matching names, then file name
expansion cannot take place and the file name is taken literally.
For example, if there was no file name in our current directory that
began with letter, then
the command vi letter*
could not be expanded so we would end up editing a new file called
(literally) letter*, including
the asterisk. Not what we wanted.
What if we have a sub-directory called
letters? If there were the three files letter.chris,
letter.david, we could get to them by typing
vi letters/letter*. This would expand to be:
The same rules for path names with commands also apply with files
names. The command vi
letters/chris.letter is the same as
vi ./letters/letter.chris which is the same as vi
/usr/jimmo/letters/letter.chris. This is because the shell is
doing the expansion before it is passed to the command. Therefore,
even directories are expanded. Therefore, the command vi
le*/letter.* could be expand as both letters/letter.chris
The next wildcard is ?.
This is expanded by the shell as one, and only one, character. For
example, the command vi
letter.chri? is the same as
vi letter.chris. However, if we were to type in vi
letter.chris? (note that the ?
comes after the s in
chris), the result would
be that we would begin editing a new file called (literally)
letter.chris?. Again, not
what we wanted. Also if there were two files
letter.chris2, the command
vi letter.chris? would be the same as:
vi letter.chris1 letter.chris2
Another commonly used metacharacter is actually a pair or characters:
[ ]. The square brackets are used to represent a list of possible
characters. For example, if we were not sure whether our file was
called letter.chris or
letter.Chris We could type
in the command as: vi
letter.[Cc]hris. So, no matter if the file was called
letter.Chris we would find
it. What happens if both files exists? Just as with other
metacharacters both are expanded and passed to vi.
Note that in this example vi
letter.[Cc]hris is the same as vi
letter.?hris, but it is not always so.
The list that appears inside the '[ ]' does not have to be upper- and
lowercase versions of the same letter. They can be any letter, number
or even punctuation. (Note that some punctuation marks have special
meaning, such as *,?,
which we will cover shortly) For example, if we have five files,
letter.chris1 - letter.chris5,
we could edit to all of them with vi
An nice thing about this list is that if it is consecutive, we don't
need to list them all. Instead, we can use a dash (-) inside the
brackets to indicate that we meant a range. So, the command
vi letter.chris could be shortened to be vi
letter.chris[1-5]. What if we only wanted, the first three and
the last one? No problem, we could specify it as vi
letter.chris[1-35]. This does not mean that we want files
letter.chris35! Rather, we
letter.chris2, letter.chris3, and letter.chris5.
This is because these are all seen as individual characters.
Inside the brackets, we are not limited to just numbers or just
letters. we can use both. The command 'vi letter.chris[abc123]' has
the potential for editing six files: letter.chrisa,
letter.chrisb, letter.chrisc, letter.chris1, letter.chris2, and
If we are so inclined, we can mix and match any of these
metacharacters any way we want. we can even use them multiple times
in the same command. Let's take as an example the command vi
*.?hris[a-f1-5]. Should they exists in our current directory,
that would match all of the following:
letter.chrisa note.chrisa letter.chrisb note.chrisb letter.chrisc
note.chrisc letter.chrisd note.chrisd letter.chrise note.chrise
letter.chris1 note.chris1 letter.chris2 note.chris2 letter.chris3
note.chris3 letter.chris4 note.chris4 letter.chris5 note.chris5
letter.Chrisa note.Chrisa letter.Chrisb note.Chrisb letter.Chrisc
note.Chrisc letter.Chrisd note.Chrisd letter.Chrise note.Chrise
letter.Chris1 note.Chris1 letter.Chris2 note.Chris2 letter.Chris3
note.Chris3 letter.Chris4 note.Chris4 letter.Chris5 note.Chris5
Also, any of these names without the leading letter
or note would match. Or if
we wanted and issues the command:
vi *.d*, this would match:
letter.daniel note.daniel letter.david note.david
Remember a moment ago, I said that the shell expands the
metacharacters only with respect to the name specified. This
obviously works for file names as I described above, however, it also
works for command names as well.
If we were to type dat*
and there was nothing in our current directory that started with
dat, we would get a message like:
dat*: not found
However, if we were to type
/bin/dat*, the shell can successfully expand this to be
/bin/date, which it would
then execute. The same applies to relative paths. If we were in /
and entered ./bin/dat* or
bin/dat* both would be
expanded properly and the right command would be executed. If we
enter the command, /bin/dat[abcdef]
we get the right response as well since the shell tries all six and
finds a match with /bin/date.
An important thing to note is that the shell expands as long as it
can before it attempts to interpret that command. I was reminded of
this fact by accident when I input
bin/l*. This yielded the output:
At first, I expected each one of the files in
/bin that began with an l
(ell) to be executed. Then I remembered that expansion takes
place before the command is interpreted. Therefore, the
command that I input, /bin/l*
was expanded to be:
/bin/l /bin/lc /bin/ld /bin/lf
/bin/line /bin/list /bin/ln /bin/login /bin/lone-tar
/bin/lr /bin/ls /bin/lx
Since /bin/l was the first
command in that list, I ended up with a long listing of all the files
in that list. The same kind of thing happened when I input /bin/l?
. However, the output looked like this:
/bin/ld /bin/lf /bin/ln
/bin/lr /bin/ls /bin/lx
This is because the command was interpreted to be:
/bin/lc /bin/ld /bin/lf
/bin/ln /bin/lr /bin/ls /bin/lx
Or, when I input /bin/l[abcdef],
This was expanded as:
/bin/lc /bin/ld /bin/lf
Keep in mind that the shell interprets the first thing on each line
as being the command. The only way for the shell to see each of these
as individual commands would be to type them all out with a
semi-colon (;) to separate them. For example,
/bin/lc; /bin/ld; /bin/lf
This would cause the system to execute /bin/lc
followed by /bin/ld and
then by /bin/lf.
I first learned about this aspect of shell expansion after about a
couple of hours of trying to extract a specific sub-directory from a
tape that I had made with the cpio
command. Since I made the tape using absolute paths, I attempted to
restore the files as /usr/jimmo/letters/*.
Rather than restoring the entire directory as I expected, it did
nothing. It worked its way through the tape until it got to end then
rewound itself without extracting any files.
At first I assumed I made a typo so I started all over. This time
checking the command before I sent it on it's way. After half an hour
or so of whirring, the tape was back at the beginning. Still no
Then it dawned on me, I hadn't told the cpio
to overwrite existing files unconditionally. So I started it all over
Now those of you who know cpio
realize that this wasn't the issue either. At least not entirely.
When the tape got to the right spot it started overwriting everything
in the directory (as I told it to). However, the files that were
missing (the ones that I really wanted to get back) were still not
copied from the backup tape.
The next time, I decided to just get a listing of all the files on
the tape. Maybe the files I wanted were not on this tape. After a
while it reached the right directory and lo and behold, there were
the files that I wanted. I could see them on the tape, I just
couldn't extract them.
Well, the first idea that popped into my mind was to restore
everything. That's sort of like fixing a flat by buying a new
car. Then I thought about restoring the entire tape into a temporary
directory where I could then get the files I wanted. Even if I had
the space, this still seemed like the wrong way of doing things.
Then it hit me. I was going about it all the wrong way. The solution
was to go ask someone what I was doing wrong. I asked one of the more
senior engineers (I had only been there less than a year at the
time). When I mentioned that I was using wildcards, it was
immediately obvious what I was doing wrong. (Obvious to him, not to
Let's think about it for a minute. It is the shell that does
the expansion, not the command itself. Like when I ran
/bin/l*. The shell interprets the command as starting with
/bin/l. Therefore, I get
the listing of all the files in /bin
that start with 'l'. With cpio
, the situation is similar.
When I first ran it, the shell interpreted the files
passing them to cpio.
Since I hadn't told cpio
to overwrite the files, it did nothing. When I told cpio
to overwrite the files, it only did so for the files that it was told
to. That is, only the files that the shell saw when it expanded
other words, cpio did what
it was told. I just told it to do something that I hadn't expected.
The solution is to find a way to pass the wild cards to cpio.
That is, have the shell ignore the special significance of the
asterisk. Fortunately there is away. By placing a backslash '\'
before the metacharacter, you remove its special significance. This
is referred to as "escaping" that character.
So, in my situation with cpio, when I referred to the files I wanted
as /usr/jimmo/data/\*, the
shell passed the arguments to cpio
as /usr/jimmo/data/*. It
was then cpio that
expanded the * to mean all
the files in that directory. Once I did that, I got the files I
Another interesting thing happens as a result of this wildcard
expansion. The maximum number of characters that the shell can
process on the command line is 5120. This seems like a lot (and it
is), but some circumstances cause commands to run into this limit.
SCO recognized this as a problem and increased the limit to 100,000
bytes by default.
Let's look at the directory /usr/include/sys.
This contains files that are used by the system when making a copy of
the kernel as well as by programmers. On my system, this directory
contains over 200 files. If I change directories there and do a
listing (ls), I see all
the files and directories under /usr/include/sys.
If I do an ls *, I also
see a list of all the files and directories. All is as it should be.
Now, if we run ls
/usr/include/sys. What do we get? What we expect: a list of
all the files and directories in /usr/include/sys.
If, however, we run ls
/usr/include/sys/* we get:
sh: /bin/ls: arg list too long
Well, it all has to do with the wild card expansion. Whenever we run
ls with no arguments as
with ls /usr/include/sys
or changing directories and simply running ls,
it is up to ls to scan the
directory to give me the output. However, whenever we use wildcards,
the shell must first process the command before it's
handed off to ls. When we
are in /usr/include/sys,
and run ls *, there is no
problem. Each of the files in /usr/include/sys
is expanded as if we had typed in:
l Sdsk.h Srom.h ... vwdisp.h
All told, this works out to less than 2500 characters. (At least on
On the other hand, if we run ls
/usr/include/sys/* this is the same as if we had typed in:
/usr/include/sys/Srom.h ... usr/include/sys/vwdisp.h
The total number of characters here is over 5700. This is well over
the maximum number of characters per line. (Actually, it's the
exec() system call that
fails and not the shell itself. If that doesn't mean anything to you.
Don't worry, it not important to understanding this limitation.)
Another aspect of this is what happens when you use this same example
on a smaller directory. For example if we did a listing like this:
All we would see is the names of the files. If instead, we did it
We see the directory names as well. This is as if we had done:
In OpenServer, things are slightly different. The shell interacts
with the command that is being executed. If the system finds that an
asterisk is appropriate to pass to the command, it will do so and the
asterisk is expanded by the command.
Another symbol that has special meaning is the dollar-sign ($). This
is used as a marker to indicate that something is a variable. I
mentioned earlier in this section that you could get access to your
login name environment variable by typing:
The system stores your login name in the environment variable
(note no '$'). The system needs some way of knowing that when
you input this on the command line, you are talking about the
variable LOGNAME and not
the literal string LOGNAME.
This is done through the '$'. There are several variables that are
set by the system. You can also set variables yourself and use them
later on. I get into more details about shell variables later on.
So far we have been talking about metacharacters used for searching
the names of files. However, metacharacters can often be used in the
arguments to certain commands. One example is the grep
command, which is used to search for strings within files. The
name grep comes from
Global Regular Expression Print. As its name implies, it has
something to do with regular expressions. Let's assume we have a text
file called documents and we wish to see if the string "letter"
exists in that text. The command might be:
grep letter documents
This will search for and print out every line containing the string
"letter." This include such things as "letterbox,"
"lettercarrier," and even "love-letter." However,
it will not find "Letterman," since we did not tell grep to
ignore upper and lower case (using the -i option). To do so using
regular expressions, the command mind look like this:
grep [Ll]etter documents
Now, since we specified to look for either "L" or "l"
followed by "etter," we get both "letter" and
"Letterman." We can also specify that we want to look for
this strings only when it appears at the beginning of a line using
the caret (^) symbol. For example:
grep ^[Ll]etter documents
This searches for all strings that start with the "beginning-of-line"
followed by either "L" or "l" followed by
"etter". Or, if we want to search for the same things at
the end of the line, we would use the dollar-sign to indicate the end
of the line. Note that the beginning of a strings the dollar-sign
will be treated as the beginning of a string, whereas at the end of
strings, it indicates the end of the line. Confused? Let's look at an
example. Let's define a string like this:
If we echo that strings we simply get ^[Ll]etter. Note that this
includes the caret at the beginning of the strings. When we do a
search like this:
grep $VAR documents
It is equivalent to:
grep ^[Ll]etter documents
Now, if write the same command like this:
grep $VAR$ documents
This says to find to the strings defined by the VAR
variable(^[Ll]etter) , but only if it is at the end of the line. Here
were have an example, where the dollar-sign has both meanings.
If we then take it one step further:
grep ^$VAR$ documents
This says to find the strings defined by the VAR variable, but only
if it takes up the entry line. In other words, the line consists only
of the beginning of the line (^), the string defined by VAR, and the
end of the line ($).
One last issue that causes it's share of confusion is quotes. In SCO
UNIX there are three kinds of quotes. They are referred to as
double-quotes ("), single-quotes (') and
back-quotes(`) (also called back-ticks). On most US keyboards,
the single- and double-quotes are on the same key, with the
double-quotes accessed by pressing shift and the single-quote key.
Usually this is on the right side of the keyboard next to the enter
key. The back-quote is usually in the upper left hand corner of the
keyboard, next to the '1'.
To best understand the difference between the behavior of these
quotes, I need to talk about them in reverse order. So we'll be
talking first about the back-quotes or back-ticks.
When enclosed inside of back-ticks, the shell interprets that as
meaning "the output of the command inside of the back-ticks."
This is referred to as command substitution as the output of
the command inside the back-ticks is substituted for the command
itself. This is often used to assign the output of a command to a
variable. As an example, let's say we wanted to keep track of how
many files are in a directory. From the command line we could say:
ls | wc
command gives me a word count, along with the number of lines
and number of characters. Here, the command might come up as:
7 7 61
However, once the command is finished and the value has been output,
we can only get it back again by re-running the command. Instead, If
Then the entire line of output would be save in the variable count.
If we then say echo $count,
7 7 61
Showing me that count now
contains that line. If we wanted, we could even assign a multi-line
output to this variable, we could use the ps
command, like this:
Then type in:
Which gives me:
PID TTY TIME COMMAND 209 06 0:02
ksh 1362 06 0:00 ps
This is different from the output that ps
would give when not assigned to the variable trash:
The next kind of quote, the single-quote ('), tells the system not to
do any expansion at all. Let's take the example above, but
this time turn the quotes around and use single quotes:
If we were to now type echo
$count, we get
What we got was exactly what we expected. The shell did no expansion
and simply assigned the literal string
"ls | wc" to the variable count.
This even applies to the variable operator '$'. For example, if we
What comes out on the screen is:
No expansion is done at all and even the '$'
is left unchanged.
The last set of quotes is the double-quote. This has partially the
same effect as the single-quotes, but to a limited extend. If we
include something inside of double-quotes, everything looses it's
special meaning except for the variable operator ($), the back-slash
(\), the back-tick (`) and the double-quote itself. Everything else
takes on it's absolute meaning. For example, we could say:
which gives us:
Wed Feb 01 16:39:30 PST 1995
This is a round-about way of getting the date, but it is good for
demonstration purposes. Plus, I often use this in shell scripts, when
I want to log something. Remember that the back-tick first expands
the command (by running it) and then the echo echoes it to the
That pretty much wraps up the characters that have special meaning to
the shell. You can get more details from the User's Guide if you need
it, but the best way to see what's happening is to try a few
combinations and see if they behave as you would expect.
A little while ago, I mentioned that some punctuation marks had
special meaning. We already know about the special meaning of
*, ?, and [
]. What about the others? Well, in fact, most of the other
punctuation marks have special meaning. I get into them in more
detail when I talk about shell programming.
Pipes and Redirection
Perhaps the most commonly used character is |,
which is referred to as the pipe symbol, or simply pipe. This enables
you to pass the output of one command through the input of another.
For example, say you would like to do a long directory listing of the
/bin directory. If you simply type ls
-l then press return, the names flash by much too fast for you
to read. When it finally stops, all you see is the last 20 or so.
If instead we ran the command ls
-l | more, we say that the output of the ls
command is "piped through more".
In this way we can scan through the list a screenful and a time.
Remember from our discussion of standard input and standard output in
chapter 1? Standard input is just a file that usually points to your
terminal. Standard output is also a file that usually points to your
terminal in this case. The standard output of the ls
command is changed to point to the pipe and the standard input of the
more command is changed to
point to the pipe as well.
The way this works is that when the shell sees the pipe symbol is it
creates a temporary file on the hard disk. Although it does not have
a name or directory entry, it does take up physical space on the hard
disk. Since both the terminal and the pipe are seen as files from the
perspective of the operating system, all we are saying is that the
system should use different files instead of standard input and
Under SCO UNIX (as well as other UNIX dialects) there are the
concepts of standard input, standard output and standard error. When
you log in and are working from the command line, standard input is
your terminal keyboard and both standard output and standard error
are the terminal screen. In other words, the shell expects to be
getting it's input from the keyboard and show the output (and any
error messages on the terminal screen).
Actually, the three (standard input, standard output and standard
error) are references to files that the shell automatically opens.
Remember that in UNIX everything is treated as a file. When the shell
starts, the three files it opens are usually the ones pointing to
When we run a command like cat,
it gets input from a file that it displays to the screen. Although it
may appear that the standard input is coming from that file, the
standard input (referred to as stdin) is still the keyboard. This is
why when the file is large enough and more
stops after each page, you can continue by pressing either the
spacebar or enter key. That's because standard input is still
As it is running, more is
displaying the contents of the file to the screen. That is, it is
going to standard output (stdout). If you try to do a more
on a file that does not exists, the message:
file_name: No such file or
shows up on your terminal screen as well. However, although it
appears to be the same place, the error message was written to
standard error (stderr). (We'll show this is different shortly)
One pair of characters that is used quite often,
< and > also
deal with stdin and stdout. The more common of the two, >
redirects the output of a command into a file. That is, it changes
standard output. An example of this would be ls
/bin > myfile. If we were to run this command, we would
have a file (in my current directory) named
myfile that contained the output of the ls
/bin command. This is because stdout is the file myfile
and not the terminal. Once the command completes, stdout
returns to being the terminal.
Figure 0-2 File
Now, we want to see the contents of the file. we could simply say
more myfile, but that
wouldn't explain about redirection. Instead, we input more
<myfile. This tells the more
command to take it's standard input from the file myfile
instead of the keyboard or some other file. (Remember even
when stdin is the keyboard, it is still seen as a file.)
What about errors? As I mentioned, stderr appears to be going
to the same place as stdout. A quick way of showing that it doesn't
is by using output redirection and forcing an error. If wanted to
list two directories and have the output go to a file, we run this
ls /bin /jimmo > /tmp/junk
we get a message:
/jimmo not found
However, if we look in /tmp,
there is, indeed, a file called junk
which contains the output of the ls
/bin portion of the command. What happened here was that we
redirected stdout into the file /tmp/junk.
Which it did with the listing of /bin.
However, since there is no directory /jimmo
(at least not on my system), we got the error /jimmo
not found. In other words, stdout went into the file, but
stderr still went to the screen.
If we want to get both the output and any error messages to go to the
same place, we can do that. Using the same example with ls
the command would be:
ls /bin /jimmo > /tmp/junk
The new part of the command is 2>&1. This says that file
descriptor 2 (stderr) should go to the same place as file descriptor
1 (stdout). By changing the command slightly:
ls /bin /jimmo > /tmp/junk
We can tell the shell to send any errors some place else. You will
find it quite often in shell scripts thoughout the system that the
file that error messages are send to /dev/null.
This has the effect of ingoring the messages completely. They
are neither displayed on the screen nor sent to a file.
Note that this command does not work as you think:
ls /bin /jimmo 2>&1 >
The reason is that we redirect stderr to the same place as stdout
before we redirect stdout. So, stderr goes to the screen, but
stdout goes to the file specified.
Redirection can also be combined with pipes like this:
sort < names | head
ps | grep sh > ps.save
In the first example, the standard input of the sort command is
redirected to point to the file names.
It's output is then passed to the pipe. The standard input of the
head command (which takes
the first 10 lines) also comes from the pipe. This would be the same
as the command:
sort names | head
In the second example, the ps
command (process status) is piped through grep
and the output of the whole thing is redirected to the file
Figure 0-3 Data
flow through a pipe
If we want to redirect stderr, we can. The syntax is similar, but it
differs slightly from shell to shell. Therefore, I am going to wait
until I talk about the individual shells.
It's possible to input multiple commands on the same command line.
This can be accomplished by using a semi-colon (;) between commands.
I have used this on occasion to create command lines like this:
man ksh | col -b > man.tmp;
vi man.tmp; rm man.tmp
This command redirects the output of the man-page for ksh
into the file man.tmp.
(The pipe through col -b is
necessary because of the way the man-pages are formatted). Next, we
are brought into the vi
editor with the file man.tmp.
After I exit vi, the
command continues and removes my temporary file man.tmp.
(After about the third time of doing this, it got pretty monotonous,
so I create a shell-script that did this for me. I'll talk more about
Interpreting the Command
One question that I had was "in what order does everything gets
done?" We have shell variables to expand, aliases and functions
to process, "real" commands, pipes and input/output
redirection. There are a lot of things that the shell has to consider
when figuring out what to do and when.
For the most part this is not so important. Commands do not get so
complex that knowing the evaluation order becomes an issue. However,
on a few occasions I have run into situations were things did not
behave as I thought they should. By evaluating the command myself (as
the shell would) it became clear what was happening.
Let's take a look.
The first thing that gets done is for the shell to figure out how
many commands there are on the line. (Remember you can separate
multiple commands on a single line with semi-colon.) This process
determines how many tokens there are on the command line. In
this context a token could be an entire command or it could be a
control word such as 'if'. Here, too, the shell has to deal with
input/output redirection and pipes.
Once it determines how many tokens it checks the syntax of each of
the tokens. Should there be a syntax error, the shell will not try to
start any of the commands. If the syntax is correct, it begins
interpreting the tokens.
The first thing it checks for is aliases. Aliases are a way for some
shells to allow you to define your own commands. If any of the tokens
on the command line is actually an alias that you have defined, it is
expanded before the shell proceeds. If it happens that an alias
contains another alias, they are expanded before continuing with the
next step. Here, functions are expanded. Like functions in
programming languages like C, a shell function can be thought of a
small sub-program. We'll get into both aliases and functions shortly.
Once aliases and functions have all been completely expanded, the
shell evaluates variables. Finally, it uses any wildcards to expand
them to file names. This is done according to the rules we talked
After it has evaluated everything, it is still not ready to
run the command. It first checks to see if the first token represents
a command built into the shell or an external one. If it's not
external, the shell needs to go through the search path.
At this point, it sets up the redirection, including the pipes. These
obviously have to be ready before the command starts since the
command may be getting it's input from some place other than the
keyboard and may be sending it somewhere other than the screen. Figure 0-4
shows how the evaluation looks graphically.
Note: This is an oversimplification. Things do happen in this order,
however there are many more things that occur in and around the steps
I have listed here. What I am attempting to describe is the general
process that occur when the shell is trying to interpret your
shell has determined what each command is and has determined that it
is an executable binary program (not a shell script) the shell
makes a copy of itself using the fork()
system call. This copy is a "child" process of the shell.
It then uses the exec()
system call to overwrite itself with the binary it wants to execute.
(We talked more about the fork-exec pair earlier). Keep in mind that
even though the child process is executing, the original shell is
still in memory, waiting for the child to complete (Assuming the
command was not started in the background with &)
If the program that needs to be executed is a shell script, the
program that is fork-exec'ed()
is another shell. This new shell starts reading the shell script and
interprets it, one line at a time. This is why a syntax error in a
shell script is not discovered when the script is started, but rather
when the erroneous line is first encountered.
Understanding that a new process is created when you run a shell
script helps to explain a very common misconception under UNIX. When
you run a shell script and that script changes directories, your
original shell knows nothing about that change. This confuses a lot
of people who are new to UNIX as they come from the DOS world where
changing the directory from within a batch file does change
it. This is because DOS does not have the same concept of a process
as UNIX does.
Look at it this way: The sub-shell's environment has been changed in
that the current directory is different, but this is not
passed back to the parent. Like "real" parent-child
relationships, only the children can inherit characteristics from
their parent, not the other way around. Therefore, any changes to the
environment, including directory changes are not noticed by the
parent. Again, this is different from the behavior of DOS .bat
can get around this by either using aliases or shell functions
(assuming that your shell has them). Another way is using the dot (.)
command in front of the shell script you want to execute. For
. myscript ¬NOTICE
script will then be interpreted directly by the current shell,
without forking a sub-shell. If the script makes changes to
the environment, then it is this shell's environment that is
You can use this same behavior if ever you need to reset your
environment. Normally, your environment is defined by the startup
files in your home directory. On occasion, things get a little
confused (maybe a variable got changed or removed) and you need to
reset things. By using the . (dot) command you can reset your
environment. For example, with either sh
or ksh you can do
it like this:
using a function of ksh
you can also do
uses the tilde (~),
which I haven't mentioned, yet. Under the ksh,
this is a shortcut way of referring to a particular user's home
directory. I'll talk more about this in the section on ksh.
you have csh, the command
is issued like this:
The Different Kinds of Shells
The great-grandfather of all shell is /bin/sh,
called simply sh or the
Bourne-Shell. (Named after it's developer Steven Bourne) This is the
'standard' shell and the one that you will find on every version on
UNIX (At least all the ones I have seen). Although many changes have
been made to UNIX, sh has
remained basically unchanged.
All the capabilities of 'the shell' I've talked about so far apply to
sh. Anything I've talked
about that sh can do, the
others can do as well. So rather than going on about what sh
can do (which I already did), I am going to talk about the
characteristics of the other two shells, csh
SCO also includes both a visual/menu driven shell, 'scosh',
as well as "restricted" versions of the other shells. I
think of scosh as more of
an application than a shell, like sh,
ksh or csh.
Therefore, we will therefore need to leave it for another time and
place. The restricted versions of each shell all have the same basics
characteristic of their non-restricted counterpart. I get into this a
little in chapter 4. In addition, there are many different shells
that are either available as public domain, shareware, or commercial
products that can be installed on SCO UNIX. However, since they are
not provided with the base system, we will need to skip them.
Evaluating a command
In the section on shell basics, we talked about environment
variables. As I mentioned these are set up for you as you are logging
in or you can set them up later. Depending on what shell you use, the
files used and where they are located is different. Some variables
are made available to everyone on the system and are accessed through
a common file. Others reside in the user's home directory. Normally,
those residing in the user's home directory can be modified. If you
can't, then maybe the system administrator has a reason or there is
some other problem. Talk to them about it.
One convention I will be using here is how I refer to the different
shells. Often I will say "the csh"
to refer to the C-Shell as a concept and not the program
/bin/csh or to "the sh"
to refer to the "Bourne Shell" as an abstract entity and
not specically to the program /bin/sh.
Most of the issues I am going to address here are detailed in the
appropriate man-pages and other docs. Why cover them here? Well, in
keeping with the basic premise of this book, I want to show you the
relationships involved. In addition, many of the things I am going to
talk about are not emphasized as much as they should. Often users
will go for months or years without learning the magic that these
shells can do. Since they "looked at" the manuals but never
read them cover to cover, they missed some of these things. Things I
feel are too exciting to miss out on.
There is one oddity that really needs addressing. This is behavior of
the different shells when moving through symbolic links. As I
mentioned before, symbolic links are simply pointers to files or
directories elsewhere on the system. If you change directories into
symbolic link, then your "location" on the disk is
different than what you might think. In some cases, the shell
understands the distinction and hides from you the fact that you are
somewhere else. This is where the problem lies.
Although the concept of symbolic links exists in ODT, it was not as
wide spread as in OpenServer. OpenServer uses the symbolic link as a
key administrative tool. The directories I will be talking about here
as symbolic links only exist in OpenServer. Let's take the directory
/usr/adm as an example.
Since it contains a lot of administrative information it is a useful
and commonly accessed directory. This is actually a symbolic link to
/var/adm. If we are using
sh as our shell, when we
do a cd /usr/adm and then
pwd, the system responds
with: /var/adm. This is
where we are "physically" located, despite the fact we did
a cd to /usr/adm. If we do
cd .. (to move up to our
parent directory) we are now located in /var.
All this seems logical. This is also the behavior of csh.
However, if we use ksh things
are different. This time when we do a cd /usr/adm
and then pwd, the system
responds with: /usr/adm.
This is where we are "logically". If we now do a cd
.., we are located in /usr.
Which of these is the "correct" behavior? Well, I would say
both. There is nothing to define what the "correct"
behavior is. Depending on your personal preference, either is
correct. I tend to prefer the behavior of ksh
since I wanted to be in /usr/adm
and not /var/adm. However,
the behavior of sh and csh
is also valid.
One of the first "new" shells to come around was the csh
or 'C-Shell'. It is so name 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, so don't get your dandruff up if it's not
exactly the same)
The csh is normally the
shell that users get on 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. Since they don't know any other
shells, the csh is a good
place to start.
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 set and the actions you
want to occur each time you log in. This can include setting you
terminal type or warning you that your password is about to expire.
In both of these files, settings variables has a syntax unique to the
csh. This is one major
difference between the csh
and the other two shells and why it is not a good idea to give root
csh as it's default shell.
The syntax for csh is:
Whereas for the other two it is simply:
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 any time you do a shell
escape from a program. What a "shell escape" is
starting a shell as a sub-process of that program. Remember in our
discussion of operating system basics we talked about the concept of
processes. As a rather contrived example, I described a case where
you would start vi, then
"jump out". This "jumping out" is called a shell
escape as you "escape" from your current program to get to
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. Since there is no default, the variable must
be set to a shell. If set to something else, you end up with an error
message like vi gives you:
invalid SHELL value:
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 access personal alias from inside of
What are "personal aliases"? No, this isn't the ability to
call yourself Thaddeus Jones when your real name is Jedediah Curry.
However, it is the ability to use a different name for a command. In
principle, they can be anything you want. They are special names that
you define to accomplish tasks. These aren't shell scripts, as a
shell script is external to your shell. To start up a shell script,
you type in it's name, the system starts a shell as a child process
of your current shell in order to run the script.
Aliases, too, are started by typing them in. However, they are
internal to the csh. That
is, they are internal to your csh
process. Instead of starting a sub-shell, the csh
executes the alias internally. This has the obvious advantage of
being quicker as there is no overhead of starting the new shell or
searching the hard disk.
Another major advantage is the ability to create new commands.
Granted you can do that will shell scripts (which we get into later),
but the overhead of creating a new process does not make it worth
while for simple things. Aliases can be created with multiple
commands strung together. For example, I created an alias 't'. That
shows me the time. Although the date command does that, all I want to
see is the time. So, I created an alias 't', which I defined like
alias t=`date | cut -c12-16'
When I type in 't', I get the hours and minutes, just exactly the
away I want it.
Aliases can be defined in either the .login
or the .cshrc. However, as
I described above, if you want them for all sub-shells, they need to
go in .cshrc. If you are
running a Bourne-Shell, aliasing may be the first good reason to
switch to another shell.
However, the sh does have
the means of creating new commands. This is done by creating shell
functions. Shell functions are just like those in a programming
language. Set of commands are grouped together and jointly called by
a single name. The csh can
create them as well.
The format for functions is the same for all three shells:
first thing to do
second thing to do
third thing to do
Functions can be defined anywhere, this include from the command
line. All you need to do is simply type in the lines one at a time,
similar to the way shown above. The thing to bear in mind is that if
you type it from the command line, once you exit that shell, the
function is gone.
Be careful when creating aliases or functions so that you don't
redefine existing commands. Either you end up forgetting the alias or
some other program uses the original program and fails since the
alias gets called first. I once had a call where the customer had a
system where he could no longer install software. The custom
program was failing. So, we tried replacing the custom
program, but that didn't work. We tried replacing the files in
/etc/perms, but that didn't work. Since it was an SCO product he was
trying to install, I assumed that it was defective media.
Fortunately, he had another copy of that same product, but custom
died with the same error. It didn't seem likely that it was bad
media, too. At this point, I have been with him for almost an hour,
so I decided to hand it off to someone else. (Often a fresh
perspective is all that is needed)
About an hour later one of the other engineers comes into my cubie
with the same problem. He couldn't come up with anything either.
(Which kind of relieved me) So, he decided that he needed to research
the issue. Well, he found the exact message in the custom
source code and it turns out that this message comes when custom
cannot run the sort
command. Ah, a corrupt sort
binary. Nope! Not that easy. What else was there. As it turns
out, the customer had created an alias called sort
we he used to sort directories in a particular fashion. Since custom
couldn't work with this version of sort, it died.
So, why use one over the other? Well, if there is something that can
be done with a short shell script, then it can be done with a
function. However, there are things that are difficult to do with an
alias. One thing is making long, relatively complicated commands.
Although you can do this with an alias, it is much simpler and easier
to read if you do it with a function. I will go into some more
details about shell functions later in the section on shell
scripting. You can also find out more details in the csh
I mentioned that aliases are a good reason to switch from sh
to csh. However, that's
not the only reason. Another advantage that the csh
has is its ability to repeat, and even edit, previous commands.
Commands are stored in a shell "history list", which, be
default contains the last 20 commands. This is normally defined in
your .cshrc file or you can do so from the command line. The command
set history=100 would change the size of your history list to
100. One thing to keep in mind is that everything you type at the
command line is save in the history file. Even if you mis-typed
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 in five minutes ago, you can check your
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 how many commands have be issued up
to that point. If you have a default csh, then your prompt is
probably a number followed by a '%,
that number is the current command. You can use that number to repeat
those previous commands. This is simply done with an exclamation mark
(!) followed by the command number as it appears in the shell
For example, if the last part of your shell history looked like this:
You could edit the letter again by simply typing in
!22. This repeats the command vi
letter.john and adds this command to your history. After you
get done editing the file, this portion of the history file would