primitive shells exercise

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 main shells you will encounter 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

Let's make a directory in which to compile all these, place the 4 source files there (compileable versions), compile them, then run and compare them. Operate as root.

cd
mkdir  shells
cd  shells
[ acquire the needed files, which are in primitiveshells.zip; acquire that file per your instructor and place it in this current directory, ~/shells ]
unzip primitiveshells.zip
gcc  dshell.c  -o  dshell
gcc  csimpleshell.c  -o  csimpleshell
gcc  simpleshell.c  -o  simpleshell
gcc  msh.c  -o  msh
gcc  hello.c  -o  hello

Understand the "Hello world" programs. Examine and run both here in your current, main shell (which is bash):

cat  hello.c
cat  hello.sh
./hello
./hello.sh

Now run each shell in succession ( e.g.,  ./simpleshell ). While in each, perform the battery of tests in the command list below and record the results.

We want to make sure each shell can at least run a command. Presumably all of them will succeed at this. They are, after all, shells ( = = programs that can). Secondly, see if they support arguments to the commands they run. If not they're lame, but we must grudgingly qualify them as (lousy) shells nonetheless. Third, can they compensate for user shortcoming by coming up with a full path that locates a command for which he gives just the command file's unqualified name (omitting its path)? Next, I want to know if these shells know how to execute shell scripts. Executing them is done by a separate mechanism than executing a compiled program (makes sense). What about changing directories? I hope they can do that. And do they keep command history? and can you "exit" to get out of them? (If not, get out of them with control-C instead.) Run the commands below in each shell, and record the results in a table like the one following the commands.

/bin/ls succeeds if you see the files that are in /root/shells
/bin/ls  -l  / succeeds if effects of / (different files) and -l (extended information) appear
ls succeeds if you see the files that are in /root/shells
/root/shells/hello succeeds if "Hello world" appears; we know it will work, /root/shells/hello is a compiled executable, fully qualified
/root/shells/hello.sh but what about a functionally equivalent shell script?
cd  / try to change directory, works or not as per next command
/bin/ls above cd worked if this shows familiar root directory contents (/etc, /home, /bin, etc.)
cd  /root/shells if cd worked, do it again to get back
/bin/ls make sure you did
history this works if it produces a list of the above commands you just ran
exit succeeds if you end up back in bash

Record the results in a table similar to the one below. While in each shell, run the tests and fill out the column for that shell with determinations whether the tested capabilities are implemented or not (instructor may give you a handout worksheet for this - capabilities.xls).

 

command capability dshell csimpleshell simpleshell msh
/bin/ls execute a command? Y/N ? Y/N ? Y/N ? Y/N ?
/bin/ls -l / support command options? Y/N ? Y/N ? Y/N ? Y/N ?
ls support path determination? Y/N ? Y/N ? Y/N ? Y/N ?
/root/shells/hello Y/N ? Y/N ? Y/N ? Y/N ?
/root/shells/hello.sh support scripting? Y/N ? Y/N ? Y/N ? Y/N ?
cd / change directories? Y/N ? Y/N ? Y/N ? Y/N ?
/bin/ls
cd /root/shells Y/N ? Y/N ? Y/N ? Y/N ?
/bin/ls
history keep command history? Y/N ? Y/N ? Y/N ? Y/N ?
exit offer a way out? Y/N ? Y/N ? Y/N ? Y/N ?

 

Installing a non-primitive shell, bash, from source code

Operate as root. Obtain the source code for bash version 4.1 from http://www.gnu.org/software/bash/. This one is not primitive. The file to get is bash-4.1.tar.gz. Download it to your home directory: :

cd
wget  http://ftp.gnu.org/gnu/bash/bash-4.1.tar.gz

Get the patches that go with it:

for i in {1..99};do wget http://ftp.gnu.org/gnu/bash/bash-4.1-patches/bash41-$(printf  "%03d"  $i); done (there are probably only 17 patch files; trying for 99 is conservative)

Install a couple needed things you may not have:

yum  install  bison
yum  install texinfo

Deploy bash-4.1.tar.gz.. This tar file is well-behaved. It creates a subdirectory and places all its contents in there, putting nothing anywhere else in your file hierarchy.

tar  -xzvf  bash-4.1.tar.gz

The directory it produces holds bash 4.1's source code, and is the "factory" for manufacturing bash from that code. Go into it:

cd  bash-4.1
ls

There are a lot of C source files there. Bash is feature-full. It's a production shell. It's major. It puts the tutorial shells to shame. Where does it get all these features? Maybe these C source files have something to do with that. Get an idea:

head  -n 3  *.c  |  less

Scan the files' self-descriptive comment lines.

Read the README file. Scan over the COMPAT, INSTALL, and doc/FAQ files to which it refers you. In INSTALL's section titled "Installation Names," note how you can specify the installation directory (where the finished-product binary executable bash file will be placed). In particular, find and read the description in INSTALL of configure's "--enable-minimal-config" and the "--prefix=<directory> options". Note:

 "The `minimal-config' option can be used to disable all of the following
 options, but it is processed first, so individual options may be
 enabled using `enable-FEATURE'."

and view the list of features that could be turned back on. (These are features that the bash shell  has beyond the Bourne shell that was its foundation.)

Let's go ahead and follow the instructions we've just read, customizing so that the new shell, once built, will be installed into our /root/shells directory along with all the others. Let's also ask that the shell be stripped back in features, close to the original Bourne shell. Many features will then not be implemented, for example command history and aliases. But as a preliminary detail, lets apply the patches we downloaded.

cat  ../bash41-??? |  patch  -p0  (this probably relies on how the shell expands "bash41-???", namely in name-numerical order, and they're probably numbered by their chronological occurrence)

Now go ahead and create the Makefile that will instruct the make utility what steps to take to build bash:

./configure --prefix=/root/shells  --enable-minimal-config

Let configure do its thing, checking out your system specifics plus command-line options to output a customized Makefile accordingly. After it's finished, you can use ls to see presence of the new Makefile. Now let's use the Makefile to build the shell (by running make).

make

When make is finished, so are all the pieces of bash 4.1. But they remain undeployed to their intended locations, within the local directory tree /root/bash-4.1. They're still in the factory. We need to ship them:

make  install

Verify that a copy of bash has appeared where you asked for it:

ls  -l  /root/shells/bin/bash

And that you can run it:

/root/shells/bin/bash

And that it is as advertised-- that is, minimal:

history
alias
echo  image.{bmp,png,jpg}

These will not work here (because they are absent from this shell). You now have a shell that approximates the Bourne shell. If you find scripts or other code written for it, you could try them against this shell. Go back to your main shell:

exit

and for fun, try the same 3 commands above to see how they are "supposed" to work:

history
alias
echo  image.{bmp,png,jpg}


You have worked with 5 shells here. You have seen that they are mere mortals, no magic, ordinary programs like any other. You have seen what they have in common. You have seen features that distinguish them as different from one another. You have a good idea what a shell is.