Introduction to Homework 3
In this lab, we will be writing a shell program. A shell is the program that all of you use when
you ssh to a linux machine, or hit
ctrl+` to open a terminal in vscode. It’s the program which
prints out a prompt, reads in what you type, and then executes whatever instruction you told it to
do. It is different from ssh (which is the program you use to connect to the server), or linux
(which is the OS on which your shell is running). The assignment is not yet posted, but the basic
loop for our (and any) shell, will be:
- Prompt the user
- Read and parse the command the user types in
- Create a new process to run the command the user typed
- Wait for the new process to end print the previous command’s exit status if the user types the command
- Start over with the prompt
The high level structure of a shell
To start this week’s lab, accept Homework 3 via GitHub. This skeleton code
is a stripped down version of the basic shell that is presented in the book in Chapter 8. In
homework 3, we will be switching it over to using
spawn instead of
fork, and then we will be
adding the ability to handle signals (the topic of this week’s lecture) and file redirection (the
topic of next week’s lecture). This week’s lab will help you do the first step.
If you’d like to go over the absolute basics of creating new processes and running different programs, you can take a look at the code that Prof. Kanich used for the fork video here.
To get started, clone the repository and get it started - as usual, you can either do this on your laptop via a devcontainer or a systemsX machine on campus.
Take a close look at
forkshell.c, and give yourself some time to understand what each function does.
evaltakes as input one string written by the user.
parselineseparates that one long string into an
argvstyle collection of pointers to the individual “words” of the command.
builtin_commandchecks to see whether the user entered a special command, like
quit. If there’s a special command, you don’t need to fork a new process, because it’s something the shell should take care of.
Suggested activity: convince yourself that you understand how
How a fork based shell works
eval has determined that it is not running a builtin command, it goes
through the process of forking a new process. Take a look at that code in
eval and make sure you
understand what it’s doing.
To dig into how those functions work, take a look at the man pages for
execve. (To view a man page, type
man fork or
man waitpid into terminal.)
How a spawn based shell works
A spawn based shell is nearly identical to a fork based shell, however spawn only returns once, and
it returns success or failure, rather than returning the process id. You will have to create a
variable to store the process id, and pass it by reference to the
Getting started on Homework 3
Accept Homework 3 if you haven’t already, and open it up. Your task for the remainder of the lab is
to modify your version of
spawnshell.c so that it uses
posix_spawnp rather than
fork. You can
take a look at the
posix_spawn man page, the fork video, and the
vscode-lectures example code to
get an idea of how to modify fork-based child process creation into a spawn-based child process
creation. Note that using
posix_spawnp makes life a little easier because it uses the
environment variable to find executables.
One very important component of all software engineering is testing. Rather than doing our testing for this assignment via the autograder right away, we will be showing you how you can test the full functionality of your shell. You’re encouraged to write your own test cases to determine whether your code is working correctly.
Using input redirection to test a shell
One important concept we’ll go over this week is file redirection. In short, file redirection allows you to read your program’s input from a file, rather than from the keyboard.
For instance, if you have a file
input that has the lines:
pwd ls date +%Y%m%d
You can feed it to a new instance of a shell program by redirecting the input from that file by
< character as an argument, then the name of the file:
ckanich@home-desktop:~/repos/shell-skel$ sh < input
Suggested activity: Ask yourself what you expect will happen when you run
sh < input, then create this file on your machine and feed it to the shell
shand see what happens.
In addition to using
sh, you can also run the skeleton code for the assignment in the same way,
and see that it works properly.
Ask yourself: how can you convince yourself that it is working properly? Hint: read the output of
man diffand try to think of a way to use that tool to determine whether your shell is running properly (note: not that it’s using posix_spawn, just that it is working as you expect it to).
Receiving redirected input vs. redirecting input ourselves
Redirected input is a tricky topic. Every process has its own input and output files, and it doesn’t care whether they are connected to terminals, files, or other cool things like network connections. By testing our shell using input redirection, our shell doesn’t need to understand input redirection. However, over the course of the next two weeks, you’ll need to add input redirection to your shell, so that, for instance, you could run a set of commands like this:
pwd ls > list_of_files cat < list of files
…and it would work the way that
sh works when given that set of inputs.
What you need to do for this lab
You can find your pairings for the lab in this sheet.
The peer explanations for this lab are:
|Session A||Draw a before and after diagram of the contents of
|Session B||Show your evaluator what you did to switch
Please grade your peers out of 1 point using this form.