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 definitions.h utilities.h |
msh.c definitions.h utilities.h |
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:
cd
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 http://teaching.idallen.org/. 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:
wget http://teaching.idallen.org/cst8129/02f/notes/simpleshell.c.txt
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:
./simpleshell
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:
exit
Didn't work. You are still in simpleshell. Find out where, exactly:
pwd
Did this one work? Go somewhere else:
cd /home
You went nowhere (running pwd again will show you). Learn from the past:
history
You didn't learn anything.
set
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:
ls
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:
/bin/ls
with this:
ls
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?)
Exiting
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.