An "elaborated" shell example program

Professor Ian Allen of Algonquin College in Ontario provides a sample mini-shell in his shell scripting course that goes a couple noteworthy steps beyond ours. His is a little less "essential"-- there is more code to look at that might distract the untutored student from the core fork-exec family functions. But it is a lot more practical-- you can actually use it. The main extensions are that it handles command arguments and is able to follow a search path so the command file's name need not be expressed in fully-qualified form. Those were big limitations on our mini-shell. On the other hand ours was 20 lines and his is 125. Before you experiment with Ian Allen's shell, please appreciate the breadth of the spectrum of shells that exist. People use the term "the shell" in the singular. That's fair enough if they mean the particular shell that they use day in and day out, probably to the exclusion of any other. In my family I talk about "the car." But I understand there are a lot of others too.

Though the most fundamental operating system in computer science, development of UNIX over its 40 years has split and branched into a thousand variations. The original AT&T code base led to several commercial flavors like HP/UX (Hewlett-Packard), AIX (IBM), and Solaris (Sun Microsystems, now Oracle). Others resulted from the very major additions to the code base by the University of California at Berkeley. Berkeley's contributions live on in non-commercial versions also, like FreeBSD, OpenBSD, NetBSD. And upstart linux arose from a fresh effort starting in 1991 that very successfully mimicked the outward behavior of a UNIX environment and its components, but without a shared codebase. Linux (which is unitary) was then delivered in many "distributions" (which are not). They include slackware, gentoo, ubuntu, fedora, debian, and many others. Not to mention academic and experimental relatives like minix and the GNU operating system. The similarities among "unices" outweigh the differences, much as with the dialects of a natural language like English. But those differences can be enough to get in the way. If you are familiar with one UNIX you are largely familiar with them all.

Under this umbrella of shifting unices lie shells, and they too are both various and shifting. The first shells written at AT&T were by Ken Thompson and Stephen Bourne. The main shells you will encounter today     are

These are all basic shells, in common, but are by no means the same as one another and by no means behave identically. In fact, feature change was the precise historical reason for the various development branches. Behavior difference is why we have a range of shells, and that's why shells don't behave exactly the same. Don't expect them to. If this comparison of command shells is the map, then you can see clearly that they are all over the map. Programs in the command shell category are as similar and different as are, more familiarly, programs in the web browser category like Mosaic, Opera, Safari, Firefox, Chrome.

As with browsers you could get a feel for shell differences from the outside looking in by discovery-- using some of the above shells and learning how they conduct business. If you were really serious you could "go inside," looking at shells' source code and putting your finger on the origin of their behaviors. For example, you can get the source code for bash here. But bash is bigger than necessary for our immediate purpose, we can make the point with 4 other shells chosen for their brevity. What's the point? the point is:

And what are the 4 shells?

These shells are all primitive, written in C with source code line counts indicated. They are short enough for us to follow their logic. We'll be able to pinpoint code responsible for behaviors they have, and recognize absence of code for those they don't. Here is the source code for the shells:

shell native (compilable) with line numbers
dshell dshell.c dshell.c
csimpleshell csimpleshell.c csimpleshell.c
simpleshell simpleshell.c simpleshell.c
mini-shell msh.c


The exercise to perform:

Obtain simpleshell.c. You may find it in the /home/public directory of the remote or virtual server you use in this class. In that case you could obtain a copy in your home directory:

cp  /home/public/simpleshell.c  .
       [ don't omit the trailing dot, it represents the destination directory for the copy and is shorthand for "the current working directory" ]

Or if you want to get it from the source, visit the page of Professor Ian Allen at Click the link there to his 2002 shell scripting course page. The link is entitled "CST 8129". Press the "Notes by Date" button and on the resulting page find and download "simpleshell.c.txt" dated Sep 14. Alternatively go directly to it:


Once you get it, rename it:

mv  simpleshell.c.txt  simpleshell.c

and just to be safe sanitize it a little:

dos2unix  simpleshell.c

Compile it:

gcc  simpleshell.c  -o  simpleshell

and run it:


Now you're in simpleshell, not bash anymore.

Differences between simpleshell and bash

Execute the following commands and note the behavior, in particular the behavior that differs from bash. I'll color code these commands in some other color than the above blue, to suggest they are not akin to those as they are not being executed by bash. First, just press enter (don't give any command). Anything different from bash? Then proceed:

man  builtin

this lets you review shell builtin commands. Some of your favorites are shown: exit, pwd, cd, history, set, echo. Press "q" to exit man. Execute the following to bail out of simpleshell:


Didn't work. You are still in simpleshell.  Find out where, exactly:


Did this one work? Go somewhere else:

cd  /home

You went nowhere (running pwd again will show you). Learn from the past:


You didn't learn anything.


How about that one?

echo  "Why do some work while others don't"

Answer that question.

Look at the source code lines of interest in simpleshell.c. They are 

lines 69-72 - issue prompt and accept input
line 88ff - distribute tokens into array
line 122 - pass the array to user function "forkexec"

line 32 - define user function "forkexec"
line 35 - call fork()
lines 40 &  43 - call execvp and exit if child
line 46 - call wait if parent

You can see them (with line numbers) as:

cat  -n  simpleshell.c

however they scroll off the screen, so hand them to less:

cat  -n  simpleshell.c  |  less

It didn't work. What, exactly, didn't? So work around:

less  -N  simpleshell.c

and review the line numbers above.

Differences between simpleshell and our dshell/fork9

Execute the following commands and note the behavior that differs from our simple dshell/fork9 example.

Compare this:


with this:

ls  -l

dshell did not handle arguments. Can you pinpoint the section of simpleshell.c's source that accounts for its ability to do so?

Then compare this:


with this:


dshell did not handle a search path. Can you pinpoint the part of the source code for simpleshell.c that accounts for its ability to do so? (You can't; but where does your search lead you?)


I don't know how to exit from simpleshell. Let me know if you see a way. Meanwhile, pressing ctrl-C should do it.


To turn in:

In simpleshell make me a screenshot. First clear the screen by executing the "clear" command. Then run the following commands (which fail) in succession: exit, history, set. Take the resulting screen image at that point. Name the screenshot file "simpleshell.jpg" (or .png). Upload it to your assignments directory on the remote server.