Simple Interactive Shell Assignment

In class we saw how to use fork(), execvp(), and waitpid() so that a command can be executed in a child process, and then control can return to the parent when the child has terminated.

In this assignment you will extend this example by writing a simple shell.

The most basic functionality of an interactive shell, such as bash, is to prompt the user to enter a command, execute the command the user enters, and repeat. Most shells have more functionality as well, such as pipes and scripting language constructs, but we will only implement the most basic functionality ourselves in C.

Requirements

For full credit, make sure you follow all of these requirements:

Examples and Suggestions

See the class code section of the class site for exec.c, the example code from class.

When using printf(), output goes to standard output, or stdout. There is another output stream available called standard error, or stderr. This stream is used for printing errors, warnings, or any other informative things that are not part of the program’s main output. I have asked you to print everything that the shell prints out to stderr, which you can do using fprintf() and passing it the special stderr file descriptor. Conversion characters sequences like %d work the same with fprintf() as they do for printf(). Here is a simple example:

fprintf(stderr, "Hello, error world!\n");

You can use getline() to get the command from the user. There is a tutorial here and you can read the detailed manual by running man getline in a terminal (when reading a manual page with man you can scroll up and down with the arrow keys, and then quit by pressing q). Be sure to look at the example in the tutorial. Probably the trickiest part is that you need to pass getline() a pointer to a malloc() allocated string. This is a pointer to a pointer, so it has type char **. You need to allocate initial memory for the string using malloc(), but it does not really matter how many bytes it is because getline() will re-allocate a larger string if needed.

There are many ways that you can get the space-delimited arguments from the string that the user enters. One way is to simply loop over the characters, copying each character into the current argument array element, and then moving to the next array element when you encounter a space. You can use also use strtok(). Here is a strtok() tutorial. If you use strtok(), be sure to replace the \n in the user’s command with a \0 right off the bat. strchr() can be used to get a pointer to the first occurrence of a character in a string. These functions also have man pages.

Example Behavior

Below is an example run. On the first line I am running the shell program that I wrote, which is named simple_shell. The following lines are now interacting with my simple shell instead of with bash. The command ls is executed three times. Once without any additional arguments, once with the -l argument which causes more detailed output, and once with a filename that does not exist which causes a non-zero exit condition.

$ ./simple_shell
> ls
simple_shell  simple_shell.c
command exited with a status of 0
> ls -l
total 24
-rwxr-xr-x 1 nws nws 17136 Sep  6 14:11 simple_shell
-rw-r--r-- 1 nws nws   981 Sep  6 14:09 simple_shell.c
command exited with a status of 0
> ls this_file_does_not_exist.txt
ls: cannot access 'this_file_does_not_exist.txt': No such file or directory
command exited with a status of 2

Make sure you test your shell with some other commands. Try cat simple_shell.c to print out the contents of simple_shell.c. Make sure you try commands with more than 2 arguments as well.

Submission

Write your program in the provided simple_shell.c and push back to git-keeper. I will grade the assignment manually.