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.
Print a usage statement to stdout and exit with status 0.
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.
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.
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.
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.
./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.
./bsh -h
echo $?
0
The exit status is zero.
./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.
./bsh
(out) > head -n2 machado.txt
(out) Caminante, son tus huellas
(out) el camino, y nada más;
(out) >
Prints the first two lines.
./bsh
(out) > cat machado.txt | wc -l
(out) 10
(out) >
Has the above output.
./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.
./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.
./bsh
(out) > head -n2 machado.txt >out.txt
(out) >
Writes the first two lines of the file to out.txt.
./bsh
(out) > head -n2 <machado.txt >out.txt
(out) >
Writes the first two lines of the file to out.txt.
./bsh
(out) > head -n2 >out.txt <machado.txt
(out) >
Writes the first two lines of the file to out.txt.
./bsh
(out) > cat machado.txt | wc -l >out.txt
(out) >
Writes 10 followed by a newline to out.txt.
./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.
./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.
./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.
./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.
./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.
./bsh
(out) > grep -q camino machado.txt
(out) > last_error
(out) 0
(out) >
Has the above output.
./bsh
(out) > grep -q zapato machado.txt
(out) > last_error
(out) 1
(out) >
Has the above output.
./bsh
(out) > cat machado.txt | sed s/camino/CAMINO/g | grep -q camino
(out) > last_error
(out) 1
(out) >
Has the above output.