CSCI 415/515: Fall 2022
Systems Programming
Project 4: bsh

Due: Thu, Oct 27, 7:00am


In this project, you will implement a basic shell called bsh. bsh won't implement any of the fancy features of Linux's standard shells, such as dash or bash, but it will implement pipelines (|) and redirection of stdout (>) and stdin (<), and in this way capture the essence of a shell and be somewhat useful.

Name

bsh - basic shell

Synopsis

bsh [-h]

Options

-h, --help

Print a usage statement to stdout and exit with status 0.

I/O Redirection

To make parsing a little easier, we will require that redirection of stdin and stdout always occur at the end of the pipeline, and that there be no spaces between the operator and the filename:


        # (1) right
        cat | sed s/foo/bar/g <in.txt >out.txt
        

In (1) above, the stdin of cat is the file in.txt, and the stdout of sed is out.txt.


        # (2) wrong (spaces between operator and filename)
        cat | sed s/foo/bar/g < in.txt > out.txt
        

Note that in (2) above, bsh interprets < in.txt > out.txt as four separate arguments to the sed command.


        # (3) also wrong (operators must occur at end of pipeline)
        cat < in.txt | sed s/foo/bar/g > out.txt
        

In (3) above, bsh interprets < in.txt as two separate arguments to cat, and > out.txt as two arguments to sed.

bsh's I/O redirection syntax deviates from the behavior of typical shells like dash or bash, which bind the redirection operator to the adjacent command in the pipeline (for instance, in (2), bash would apply both redirection operators to sed; in (3), bash would bind < in.txt to cat and > out.txt to sed). We deviate simply to make the parsing less complicated.

Skeleton Code

To help get started, please use the following skeleton code:

If you're working from a Linux terminal, it's easy to download these files using the curl tool:


        curl -O https://www.cs.wm.edu/~smherwig/courses/csci415-f2022/bsh/src/bsh.c
        curl -O https://www.cs.wm.edu/~smherwig/courses/csci415-f2022/bsh/src/list.h
        curl -O https://www.cs.wm.edu/~smherwig/courses/csci415-f2022/bsh/src/mu.c
        curl -O https://www.cs.wm.edu/~smherwig/courses/csci415-f2022/bsh/src/mu.h
        

Note that bsh.c already implements a loop that prompts the user for a command, parses the command, and then prints the result of the parsing. In particular, the skeleton code already handles parsing a pipeline of commands into a linked list (represented by struct pipeline). In turn, each command is represented by a struct cmd that uses a dynamically-sized array of strings to store the command and its arguments.

Within bsh.c, there are a few TODO comments that correspond to code you need to write. In particular, you must implement the usual getopt_long parsing of bsh's options, implement parsing of the I/O redirection operators (if present, the skeleton code's parsing will have included these as the last arguments to the last command in the pipeline), and finally implement the actual evaluation of the pipeline.

Simplifying Assumptions

Other than pipes and redirection of stdout and stdin, our shell does not support any other features. In particular, we won't worry about argument quoting and variable expansion -- all of that requires a much more sophisticated parser.

Bonus 1

Implement the >>FILE redirection operator, which appends standard output to FILE. As with the other redirection operators, we will use a simplified syntax where there are no spaces between >> and FILE.

Bonus 2

Implement a shell builtin called last_error. A shell builtin is a command that the shell implements as an internal function: the shell invokes the function instead of exec'ing an external program. In general, the shell uses builtins to implement commands that would be impossible to implement as an external program (e.g., pwd), or for commands that are much more efficient to implement internally (e.g., echo).

Your last_error command is conceptually equivalent to echo $?: it prints to stdout the exit status of the last command executed, followed by a newline. If the last command executed was a pipeline of multiple commands, then last_error prints the exit status of the last command in that pipeline.

To keep things simple, we'll assume that last_error is always called by itself: it is never part of pipeline and never redirects its output.

Submitting

Submit your project as a zip file via gradescope. Your project must include a Makefile that builds an executable called bsh. Please refer to the instructions for submitting an assignment for details on how to login to gradescope and properly zip your project.

Rubric

Input File

machado.txt



      

-h, --help


1.1 Print a usage statement (3 pts)


        ./bsh --help
        

Prints a usage statement to stdout. The statement must start with either Usage or usage; you decide the rest of the message. Conventionally, this option either prints the synopsis or a more verbose statement that also includes a description of the options.

1.2 Zero exit status (2 pts)


        ./bsh -h
        echo $?
        0
        

The exit status is zero.

Single Command


2.1 cat (6 pts)


        ./bsh
(out) > cat machado.txt
(out) Caminante, son tus huellas
(out) el camino, y nada más;
(out) caminante, no hay camino,
(out) se hace camino al andar.
(out) Al andar se hace camino,
(out) y al volver la vista atrás
(out) se ve la senda que nunca
(out) se ha de volver a pisar.
(out) Caminante, no hay camino,
(out) sino estelas en la mar.
(out) >
        

Prints the file.

2.2 head (6 pts)


        ./bsh
(out) > head -n2 machado.txt
(out) Caminante, son tus huellas
(out) el camino, y nada más;
(out) >
        

Prints the first two lines.

Pipeline


3.1 cat | wc (8 pts)


        ./bsh
(out) > cat machado.txt | wc -l
(out) 10
(out) >
        

Has the above output.

3.2 cat | sed | head (9 pts)


        ./bsh
(out) > cat machado.txt | sed s/camino/CAMINO/g | head -n3
(out) Caminante, son tus huellas
(out) el CAMINO, y nada más;
(out) caminante, no hay CAMINO,
(out) >
        

Has the above output.

Single Command I/O Redirect


4.1 Redirect stdin (8 pts)


        ./bsh
(out) > cat <machado.txt
(out) Caminante, son tus huellas
(out) el camino, y nada más;
(out) caminante, no hay camino,
(out) se hace camino al andar.
(out) Al andar se hace camino,
(out) y al volver la vista atrás
(out) se ve la senda que nunca
(out) se ha de volver a pisar.
(out) Caminante, no hay camino,
(out) sino estelas en la mar.
(out) >
        

Prints the entire file.

4.2 Redirect stdout (8 pts)


        ./bsh
(out) > head -n2 machado.txt >out.txt
(out) >
        

Writes the first two lines of the file to out.txt.

4.3 Redirect stdin and stdout (9 pts)


        ./bsh
(out) > head -n2 <machado.txt >out.txt
(out) >
        

Writes the first two lines of the file to out.txt.

4.4 Redirect stdout and stdin (9 pts)


        ./bsh
(out) > head -n2 >out.txt <machado.txt
(out) >
        

Writes the first two lines of the file to out.txt.

Pipeline I/O Redirect


5.1 cat | wc > (10 pts)


        ./bsh
(out) > cat machado.txt | wc -l >out.txt
(out) >
        

Writes 10 followed by a newline to out.txt.

5.2 cat | sed | head < (10 pts)


        ./bsh
(out) > cat | sed s/camino/CAMINO/g | head -n3 <machado.txt
(out) Caminante, son tus huellas
(out) el CAMINO, y nada más;
(out) caminante, no hay CAMINO,
(out) >
        

Prints the first three lines of the file with camino in caps.

5.3 cat | sed | head < > (12 pts)


        ./bsh
(out) > cat | sed s/camino/CAMINO/g | head -n3 <machado.txt >out.txt
(out) >
        

Writes the first three lines of the file with camino in caps to out.txt.

Bonus 1: Append (>>) I/O Redirection Operator


100.1 Single Command stdout(3 pts)


        ./bsh
(out) > head -n1 machado.txt >out.txt
(out) > tail -n1 machado.txt >>out.txt
        

Writes the first line and last line the file to out.txt.

100.2 Single Command stdin and stdout(3 pts)


        ./bsh
(out) > head -n1 machado.txt >out.txt
(out) > tail -n1 <machado.txt >>out.txt
        

Writes the first line and last line the file to out.txt.

100.3 Pipeline stdout(4 pts)


        ./bsh
(out) > head -n1 machado.txt >out.txt
(out) > tail -n1 machado.txt| sed s/mar/MAR/g >>out.txt
        

Writes the first line and last line of the file to out.txt, with the last line having the word mar in caps.

Bonus 2: last_error builtin


200.1 grep succeed (3 pts)


        ./bsh
(out) > grep -q camino machado.txt
(out) > last_error
(out) 0
(out) >
        

Has the above output.

200.2 grep fail (3 pts)


        ./bsh
(out) > grep -q zapato machado.txt
(out) > last_error
(out) 1
(out) >
        

Has the above output.

200.3 Pipeline fail (4 pts)


        ./bsh
(out) > cat machado.txt | sed s/camino/CAMINO/g | grep -q camino
(out) > last_error
(out) 1
(out) >
        

Has the above output.