Project 1: Implementing a Simple Shell

(c) 1997, Howard E. Motteler

Assigned Wed Feb 5; Due Wed Feb 19; 60 pts


Project Goals

The goals of this project are to learn the basics of how a "shell" or command interpreter works, and to gain experience programming with Unix processes and systems calls.

The Project

You are to design and implement a simple shell, "mysh". A prototype is on page 24 of your text; you should start with this and expand it to implement the features specified below.

Specifications

Your shell should be able to parse and execute command lines of the following form
         cmd 
         cmd > file
         cmd < file
         cmd | cmd
where "file" is any valid unix filename, and "cmd" is a program name followed by zero or more arguments, with no wildcards, no in-line command evaluation, no macro substitutions, and which does not include the symbols ">", "<", or "|". For the special case when "cmd" is "exit", your shell should terminate.

Your shell should also be able to parse and execute command lines as above ending with an "&" that puts the specified process or processes in the background.

         cmd &
         cmd > file &
         cmd < file &
         cmd | cmd &
You can assume that all command lines are of the form described here, and you do not have to check for any other sorts of input. In particular, you do not have to expand filename wildcards, do multi-step pipes, or do both input and output redirection on one line.

Use fork() and either execve() or execvp() to execute the commands. In Minix and Linux execve() is a systems call, and execvp() a library routine; on the SGIs, both seem to be systems calls. The main difference is that execvp() will search your current PATH for the specified executable, and doesn't have an explicit environment argument.

Your shell should prompt the user for each command line with the string "mysh > ". The final version you submit should not print any test messages or other extraneous jabber. You should print errors returned by execvp() or execve(); you can use strerror(errono) to make your error reporting more informative; errno is an external variable set to the most recent error, and strerror() translates this to a human-readable string. You may find the Unix string manipulation library routines convenient for parsing the command line; do a "man string" to learn more about these functions.

To redirect input from a file to a program, so the file appears as "standard input" to the program (e.g., as in the command "wc < test.dat"), you will have to manipulate file descriptors. To connect a file to the Unix "standard input" (by convention file descriptor 0), you can use the dup2() system call, as in the following example.

    if ((fd=open(fname, O_RDONLY)) == -1) {
        fprintf(stderr, "mysh error: can't open %s\n", fname);
        exit(1);
        }
    dup2(fd, 0);
    close(fd);
    if (execvp(cmd, args) == -1)
       fprintf(stderr, "mysh error: %s\n", strerror(errno));
If we wanted to execute the command "ls -l", "cmd" in the above example would be the string "ls", and the string array "args" would have its first element pointing to "ls", its second element pointing to "-l", and its third element the null pointer 0, indicating the end of the args array.

The "&" function is easy to implement--without the "&", the shell waits for the child process; with the "&" it does not wait. The pipe function "|" is a little more difficult, and can be implemented either with a temporary file, or preferably or with a real Unix pipe.

Grading

Make sure you have read the general information on programming projects. Approximately 20% of your grade is for documentation, and the remainder is based on how well your project works. You should describe your design and implementation at the beginning of the project; this initial description is worth 15% of your grade. Describe any non-trivial data structures you have used, and briefly say how each relevant routine acts on those data structures. The description should be about a half-page long. Do not ramble incoherently, and do not simply echo the specifications given here.

The points for functionality are divided up as follows: 20% for simple commands, 20% for working i/o redirection, 20% for being able to successfully put a process in the background, and 20% for working pipes.

Do not use the system() sys call or invoke the unix shell to implement your shell!

You must do the project described here. Doing some other similar or dissimilar project, no matter how difficult or clever, may be worth 0 points. This is not a group project; please do your own work, and be careful about sharing your code. It is OK to discuss design issues, but in your documentation you should give credit to your sources.

What to turn in

Use the UCS submit program to turn in a single file "mysh.c" that contains your shell implementation, including data declarations and related procedures. The command to do this is
               submit cs421_0101 proj1 mysh.c

Hints and Tips