Part 4. Making a shell (15 points)

Congratulations, you’ve completed the difficult part! All that remains is to turn your code into a proper shell.

Here’s a short sample session with my implementation.

Welcome to the Oberlin Shell!
$ whoami
steve
$ date
Wed Oct 18 13:51:00 EDT 2023
$ echo "I can run" 'commands with' multiple' 'arguments | tr a-z A-Z    
I CAN RUN COMMANDS WITH MULTIPLE ARGUMENTS
$ ^D

To exit the shell, I pressed Ctrl-D (which it printed as ^D). This causes stdin to be closed.

Your task

Modify your main() function to print out a welcome message and then read lines of input in a loop, parse their input, build a pipeline, and run the pipeline. Any error should be printed out but the shell should not exit. If you have correctly configured stderr in Part 3, any errors that occur while a process is running will already be printed out, and no additional handling is needed for them.

If the user enters a line containing only whitespace, the line should be ignored and the prompt printed again.

I found it helpful to separate out the logic of reading a line, parsing it, and running it into a separate function which returned a Result<()> and then in main(), I have a loop that calls the function and if it returns an error, print the error out.

Tip

You can determine if stdin is closed when you read from it because it will return Ok(0). Here’s some code that reads from stdin and and exits the program if stdin is closed.

let input_length = io::stdin().read_line(&mut line)?;

if input_length == 0 {
    println!("");
    std::process::exit(0);
}