Writing Your Own Shell

The object of this assignment is to gain experience with some advanced programming techniques like process creation and contol, file descriptors, signals and possibly pipes. To do this, you will be writing your own command shell - much like csh, bsh or the DOS command shell.

You may do this assignment individually or in groups of two. If you work alone, you should develop your shell for Linux. If you work in a team, you should write a single program that can be compile to run either on Linux or on Windows. In either case, you should program in C. (In Windows, you should use the Win32 API not use Cygwin etc. :-))

Basic Psuedocode for a Shell

From experience using a command shell, you should be able to write basic pseudocode for a shell (Note: The pseducode below uses the UNIX style fork/exec not the Windows style CreateProcess/WaitForSingleObject.):

     
 

     int 
     main (int argc, char **argv)
     {
	while (1){
		int childPid;
		char * cmdLine;

	        printPrompt();

	        cmdLine= readCommandLine();
		
		if ( isBuiltInCommand(cmdLine)){
		    executeBuiltInCommand(cmdLine);
		} else {		
		     childPid = fork();
		     if (childPid == 0){
			exec ( getCommand(cmdLine));
			
		     } else {
			if (runInForeground(cmdLine)){
				wait (childPid);
			} else {
			        record in list of background jobs
			}		
		    }
	        }
     }

Required Features

Between this simple pseudocode and full featured shells, there are many optional features. Here are the features you should support in your shell:
  1. The prompt you print should indicate the current working directory. For example:
    /usr/foo/bar%
    It may also indicate the machine name, the user name or any other information you would like.
  2. You should allow the user to specify commands either by relative or absolute pathnames. To read in the command line, you may want to consider the readline function from the GNU readline library as it supports user editing of the command line. I do not know if there is an equivalent for Windows so you may end up needing to write your own version of readline for Windows. In that case, it does not need to support editing of the command line etc.
  3. You do not need to support setting of environment variables. However, you may find it useful to know about these variables especially PATH which is the list of directories in which to look for a specified executable. You may use execvp to have the system search the PATH inherited by your own shell from its parent.
  4. You should be able to redirect STDIN and STDOUT for the new processes by using < and >. For example, "foo < infile > outfile" would create a new process to run foo and assign STDIN for the new process to infile and STDOUT for the new process to outfile. In many real shells it gets much more complicated than this (e.g. >> to append, >! to overwrite, >& redirect STDERR and STDOUT, etc.)! (WARNING: I am told that I/O redirection may be quite tricky on Windows. We may substitute a different feature here for Windows. ) You also do not have to support I/O redirection for built-in commands (it shouldn't be too hard but you don't have to do it.) Note: one redirect in each direction is fine (not ls > foo < foo2) .
  5. You should be able to place commands in the background with an & at the end of the command line. You do not need to support moving processes between the foreground and the background. You also do not need to support putting built-in commands in the background.
  6. You should maintain a history of commands previously issued. The number of previous commands recorded can be a compile time constant of at least 10. You should start numbering them with 1 and then when you exhaust your buffer you should discard the lowest number *BUT* keep incrementing the number of the next item. For example, if storing 10 commands, when the 11th is issued, you would be recording commands 2 through 11.
  7. A user should be able to repeat a previously issued command by typing "!number" where number indicates which command to repeat. "!-1" would mean to repeat the last command. "!1" would mean repeat the command numbered 1 in the list of command returned by history. (Note: You can probably think of better syntax for this, but I thought it was good to stay as close as possible to syntax used by real shells.)
  8. A built-in command is one for which no new process is created but instead the functionality is build directly into the shell itself. You should support the following built-in commands: jobs, cd, history, exit and kill. jobs provide a numbered list of processes currently executing in the background. cd should change the working directory. history should print the list of previously executed commands. The list of commands should include be numbered such that the numbers can be used with ! to indicate a command to repeat. exit should terminate your shell process. "kill num" should terminate the process numbered, num, in the list of background processes returned by jobs (by sending it a SIGKILL signal). (Note: Usually "kill num" refers to the process with ProcessId, num; while "kill %num" refers to the process in the jobs list with number, num.) I would also *highly* recommend the built-in function "help" that lists the available built-in commands and their syntax. (If you don't follow the syntax expected, then a "help" function would let the graders proceed anyway.)
  9. If the user choose to exit while there are background processes, notify the user that these background processes exist and return to the command prompt. The user should kill them before exiting.
  10. You may assume that each item in the command string is seperated on either side by spaces (e.g. "cat < infile" rather than "cat

    Examples

    Here is a text file that I wrote (not computer output so beware) to show an example of the shell in operation.

    Here is a tar file that contains a Makefile that will get you started compiling on a UNIX/Linux platform. Copy the file and then execute the command "tar -xf example.tar" to unzip the contents. Then simply cd into the directory and type "make". This will compile shell.c into the executable shell. You can execute shell by typing "./shell" at the command prompt. You can remove the executable by typing "make clean". If you would like to understand Makefiles better, I have included a file funMakefile. Try "make -f funMakefile".

    Here is a tar file that contains a Makefile and two programs that illustrate the use of the dup system call to support I/O redirection. Note that the file f1 is present in the directory and is needed as the input file for the forkDupExec program. That program when run will produce f2 an identical copy of f1 by forking off a new process, reassigning its stdout and stdin and execing cat.

    Optional Features

    If you are enjoying this project and would like to add more advanced features to your shell, here are some sugguestions:

    1. You could support optional parameters to some of the built-in commands. For example, "history -s num" could set the size of the history buffer and "history num" could return the last num commands. You could also support additional built-in commands like which, pushd/popd or alias. If you make modifcations of this type, I would recommend "help command" to return more detailed information on a single command.
    2. You could support | , a pipe, between two processes. For example, "foo | bar" would send the STDOUT of foo to the STDIN of bar using a pipe. You may want to start by supporting pipes only between two processes before considering longer chains. Longer chains will probably require something like handle process n and then recursively handle the other n-1.
    3. You could implement more advanced I/O redirection as described above (>&, >!, etc.).
    4. You could implement the built-in shell functions, fg and bg, to move processes between the background and the foreground.
    5. You could support the editing of shell variables with built-in shell functions like printenv and setenv.
    6. I wouldn't recommend it :-), but you could even write support for shell programming (e.g. if/then, while, for constructs).
    7. Tab completion and command prompt editing. The GNU readline library makes this easy.
    8. Up and down errors to scroll through the history list. The GNU history library makes easy.
    9. Adding the builtin function ls on Windows.
    10. You could relax the parsing constraints on the command line itself (e.g. correctly recognize "cat
    11. Terminal support (messy!)(You may notice that some programs like more or pine are aware of the screen real estate they need - that requires terminal emulation support.
    12. You could also try porting it to yet another OS.
    Any advanced shell feature is likely to earn you some extra credit, but you should do it only if you are having fun and would like to learn more. In particular, we will *not* say how much extra credit each feature or sub feature may be worth.

    Submitting Your Assignment

    You should prepare a directory containing your source code, a README file and a Makefile. If you choose the team version (Linux and Windows) then your directory should also contain a .dsw and .dsp files.
    1. Put all the things you want to submit into a directory called "submit"
    2. cd to the submit directory's parent directory
    3. Tar it up with : tar -cvf submit.tar submit
    4. Then gzip it with: gzip submit.tar
    5. Submit the file submit.tar.gz through the course mangement system

    Your README ( in addtion to your names, etc.) should contain a clear description of the status of each required feature. If you tackled any optional features, they should be clearly described (sufficiently that we can test them). You should also write an annotated bibliography describing sources of helpful information to which you referred (see the following for an example and guidelines on appropriate borrowing).

    You should test your shell programs on the machines in the CSUG lab. In particular, you should be able to cd into your directory, type make, and then execute your shell (because that is what we will do to test it!). On Windows, we should be able to open the .dsp file and then build and execute your code. Any required settings/arguements etc. should be already set as part of the project. I recommend using a -DUNIX in the UNIX makefile and then listing WINDOWS in the Preprocessor Definition section of the Project Settings for the Windows project.

    Helpful Resources

    Here are a list of resources you may find helpful. Feel free to send mail suggesting others.

    Linux/UNIX

    GNU C Library

    GNU history library (for the required functionality might be easier without it?)

    The following functions are likely to be helpful (consult the man pages for details):
    fork, exec, execvp, wait, waitpid, kill, dup, pipe, strncmp, strlen, malloc, free, getcwd, chdir , open, close, readline, gets, fgets, getchar, signal (*not* system!)

    Windows

    MSDN

    Sample I/O redirection

    General

    Debugging with GDB

    Sample Solutions: Eric Breck and Yanling Wang's shell (tar ), Omar Khan and Murali Kumar's shell (tar )


    Last modified: Tue Sep 17 20:38:32 Eastern Daylight Time 2002