CSCI 2330
Introduction to Systems

Bowdoin College
Spring 2016
Instructor: Sean Barker

Project 5 - bsh: the Bowdoin Shell

This project should be completed individually or in groups of 2. Groups should only complete and turn in a single program.

This project will help you understand the inner workings of the shell, a program that you have been using all semester. You'll do this by writing your own simple shell program that supports Unix-style job control. Implementing your shell will teach you the core concepts of process control and signaling, as well as give you experience with system programming in C.

This project is more complex than past projects. Plan and budget your time accordingly!

Start by reading through the entire project description!

Project Overview

A shell is an interactive command-line interpreter that runs programs on behalf of the user. At a high level, a shell repeatedly prints a prompt, waits for a program name and command-line arguments on stdin, then carries out some action as directed by the input.

The shell program that you have been using all semester is bash (the Bourne Again Shell). Bash is only one of many shell programs, however - others include sh, tcsh, and csh. In this project, you will implement your own shell, bsh (the Bowdoin Shell).

Unix Shell Basics

A command-line string is a sequence of text words delimited by whitespace. The first word of the string is either the pathname of an executable file (i.e., a program) or a built-in command. The remaining words are command-line arguments. If the first word is a built-in command, the shell immediately executes the command within the current shell process. Otherwise, the shell forks a child process, then executes the specified program in the context of the child. The child processes created as a result of interpreting a single command are known as a job. A job can consist of multiple child processes connected by Unix pipes (denoted in a command by vertical bars, |).

By default, the job runs in the foreground, which means that the shell waits for the job to terminate before prompting for the next command string. Thus, at any point in time, at most one job can be running in the foreground. However, if the command string ends with an ampersanad (&), then the job runs in the background, which means that the shell does not wait for the job to terminate before printing another prompt and waiting for another command string. Thus, an arbitrary number of jobs can be running in the background at a given time.

Typing the following command runs the program ls (located in the directory /bin) in the foreground with command line arguments -l -d:

bsh> /bin/ls -l -d

Note that more specifically, calling the above will execute the main function of /bin/ls with the following values of argc and argv:

Alternately, typing the same command with an ampersand will run ls in the background:

bsh> /bin/ls -l -d &

Job Control

Unix shells support the notion of job control, which allows users to move jobs back and forth between background and foreground, and to change the process state (running, stopped, or terminated) of all the processes in a job. Job states can be changed via signals: typing Ctrl-Z causes a SIGTSTP signal to be delivered to every process in the foreground job. The default action for SIGTSTP is to place the process in the stopped state, where it remains until it is awakened by the receipt of a SIGCONT signal. Typing Ctrl-C causes a SIGINT signal to be delivered to each process in the foreground job. The default action for SIGINT is to terminate the process.

Unix shells also provide various built-in commands that support job control. Key commands are listed below:

The bsh Specification

The bsh shell should have the following features:

Code Structure

To start, you have been provided with a functional skeleton of the shell. The starting code implements a number of less interesting functions that you should use while implementing the complete shell, allowing you to focus on the more interesting components. In particular, you are responsible for implementing each of the empty functions listed below. As a sanity check, also listed below is the number of code lines implementing each function in the reference shell (which includes comments):

Note: While the function lengths given above are fairly modest, don't be lulled into a false sense of security! System programming involves writing dense, precise, and often error-prone code, and is likely to require significant debugging time. Be sure to give yourself plenty of time!

The file you should modify that contains the code of your shell is bsh.c. The included Makefile will compile the shell for you. To run your shell, simply execute it:

unix> ./bsh
bsh> [type commands to your shell here]

Included Files

You have also been provided with a number of tools to help you check your work. All included files are described below:

Use the -h flag to see the usage string for

unix> ./ -h
Usage: ./ [-hv] -t <trace> -s <shellprog> -a <args>
  -h            Print this message
  -v            Be more verbose
  -t <trace>    Trace file
  -s <shell>    Shell program to test
  -a <args>     Shell arguments

You can run the shell driver on trace01.txt by typing the following:

unix> ./ -t trace01.txt -s ./bsh -a "-p"

Similarly, to compare your result with the reference shell, you can run the trace driver on the reference shell by simply substituting bsh with bshref in the command above.

More simply, you can use the included Makefile to run the driver on the trace files. To pass trace01.txt through your shell, you can just run:

unix> make test01

Similarly, to pass trace01.txt through the reference shell, you can run:

unix> make rtest01

The output of your shell from the trace files is exactly the same as the output you would have gotten from running your shell interactively, except for an initial comment that identifies the trace.


Here are some general (and specific!) tips for working on your shell:


You are responsible for completing the contents of the bsh.c file. You should not modify any other file. You are responsible for ensuring that your program runs on the class server, regardless of where else you may be writing code.

As usual, your final submission will consist of your committed bsh.c file at the time of the due date. Each group will make only one submission, which should be committed to the group's shared SVN folder (not the SVN folders of the individual group members).

Note for groups: If you are working in a group, you must individually send me an email giving me a brief description of your and your group member's contribution to the project after your submission. The purpose of this requirement is to encourage full group participation on the project. Your email does not need to be detailed, but your project is not considered complete until your summary is received. Your email will be kept confidential by me and will not be shared (if I have any concerns, I will reach out to you).


You will be evaluated both on the correctness of your shell implementation (as determined by the 16 trace files) as well as the style and overall quality of your program (as determined by me).

Particular things to watch out for:

In addition, you should follow all standard and sensible style guidelines, such as using good variable names, commenting, using consistent indentation, etc. While you do not have to adhere to any particular set of conventions (e.g., where to put parentheses), you SHOULD be consistent with whatever conventions you choose. If you are working with a partner, agree on a single set of conventions and stick to them!

You do not need to define any functions beyond those already specified in bsh.c, but you are welcome to do so if you wish.

Please ask if you have any questions about what contitutes good style or what is expected!