BigShell Specification
In this assignment, you will explore many important process concepts by implementing a POSIX-like shell program. Your program will:
Parse command-line input into commands to be executed
Execute a variety of external commands (programs) as separate processes
Implement a variety of shell built-in commands within the shell itself
Perform a variety of i/o redirection on behalf of commands to be executed
Assign, evaluate, and export to the environment, shell variables
Implement signal handling appropriate for a shell and executed commands
Manage processes and pipelines of processes using job control concepts
Learning Outcomes
Describe the Unix process API (Module 3, MLO 2)
Write programs using the Unix process API (Module 3, MLO 3)
Explain the concept of signals and their uses (Module 3, MLO 2)
Write programs using the Unix API for signal handling (Module 3, MLO 3)
Explain I/O redirection and write programs that can employ I/O redirection (Module 3, MLO 4)
Overview
You will write a program, named BigShell, that implements a large subset of the POSIX Shell’s functionality.
The high-level functionalities you will be tasked with implementing are:
The following built-in shell utilities:
cd – Change the working directory
exit – Cause the shell to exit
unset – unset values of variables
Signal handling behavior of the shell, and commands
Command execution
Expansion of:
Command words
Assignment values
Redirection filename operands
I/O Redirection of the following operators:
>
<
<>
>>
>|
>&
<&
Variable assignment
Simple commands:
Foreground commands –
;
Background commands –
&
Shell pipelines –
|
Job control
Reference Documents
The overall assignment is loosely based on the shell command language specification in POSIX.1-2008, with several features removed or simplified.
Builtins
cd(1)
chdir(3)
exit(1)
exit(3)
strtol(3)
unset(1)
getenv(3)/setenv(3)
Signal Handling
sigaction(3)
kill(2)
signal.h(0)
signal(7)
I/O Redirection
dup(2)
fcntl(3)
open(2)
Foreground/Background Commands
wait(2)
Pipelines
pipe(2)
Job Control
isatty(3)
setpgid(3)/setpgrp(3)
getpgid(3)/getpgrp(3)
tcsetpgrp(3)/tcgetpgrp(3)
General Purpose
ctype.h(0)
errno.h(0)
Command Language
Discussion
This program is intended to be a fun project for you to learn fundamental process concepts while also building a featureful program to show off as your portfolio project. Writing a shell is very challenging–the language is difficult to parse, the specification leaves out many important details, and is often ambiguously worded, and the size of a shell’s code-base is very large. Accomplishing this on your own is a huge feat!
Neither the provided skeleton code, nor reference implementation, are 100% bug free–in fact, I have found several bugs while writing this documentation after having finished the published implementation. Similarly, your program is not expected to handle every edge case or work perfectly in every situation. There may be aspects of the specification which are ambiguous, and edge cases which produce unexpected results. You are always encouraged to use your best judgement in approaching ambiguities, and if in doubt, ask staff for guidance.
The specification is intended merely as a guideline. Unless explicitly tested by the grading rubric, any other aspects of the program, specified or not, are up to you. The TODOs listed in the skeleton code should serve as a road-map for completion of the assignment. Each TODO should explain in detail what you need to implement in order to get to the same functionality as the reference implementation.
The suggested order of implementation is:
Built-in Commands
Non-built-in commands
Foreground/Background process waiting
Redirection
Pipelines
Variable Assignment
Signal Handling
Job Control Functionality
Constraints
Your program must compile to the
release
build target with the provided makefile.
Testing Your Program
A reference implementation is provided with the assignment skeleton code. It has been tested to run in the GitHub Codespaces environment and scores full points on the grading script. It is likely to also run on any recent Linux distribution.
Rubric
The example expected test output listed below should serve as a guide. You are encouraged to change variable names, try more complex constructions, and different commands. A rubric item may not be tested exactly as shown, but gives a general idea of what to expect–we won’t try and surprise you or throw you any curve-balls.
Built-in Commands [20%]
Does the exit built-in work appropriately? [5%]
bigshell$ exit 123 bash$ echo $? 123
bigshell$ bash -c 'exit 123' # Set the $? variable to 123 bigshell$ exit bash$ echo $? 123
Does the cd built-in work appropriately? [5%]
$ echo $HOME /home/bennybeaver $ cd $ pwd /home/bennybeaver $ cd /bin $ pwd /bin
If the HOME variable is modified, does the cd utility respond appropriately? [5%]
$ cd $ pwd /home/donnyduck $ HOME=/home/bennybeaver # Supposing that /home/bennybeaver is a directory that exists $ cd $ pwd /home/bennybeaver
Does the unset built-in work appropriately? [5%]
$ echo $HOME /home/bennybeaver $ unset HOME $ echo $HOME $ X=12 $ echo $X 12 $ unset X $ echo $X $
Parameters and Variables [24%]
If a variable is set with no command words, does it persist as an internal (not exported) shell variable? [3%]
$ unset X # Ensure X isn't set yet $ X=123 $ echo $X # Show that X is expanded 123 $ printenv X # Show that X is not exported $
If a variable is exported with the export utility, does a child process that is executed have that variable set in its environment? [3%]
$ unset X $ X=123 $ export X $ printenv X 123
If a variable is set as part of a command, does the child process that is executed have that variable set in its environment (and only its environment)? [3%]
$ printenv X # Show that X is unset in the shell $ X=123 printenv X # Show that X is added to the environment of the printenv command 123 $ printenv X # Show that X remains unset in the shell $
If a foreground command is executed, does the
$?
special parameter get updated appropriately? [3%]$ true $ echo $? 0 $ false $ echo $? 1 $ sh -c 'exit 123' $ echo $? 123
If a background command is executed, does the
$!
special parameter get updated appropriately? [3%]$ sleep 10 & $ echo $! 29483 $ pgrep sleep 29483
If a foreground command is executed, does it not modify
$!
? [3%]$ sleep 10 & $ pgrep sleep 29483 $ echo "hello world" hello world $ echo $! 29483
If the
PATH
variable is changed, does it affect command lookup? [3%]$ echo test test $ PATH= $ echo test bigshell: No such file or directory
Does the cd built-in update the
PWD
shell variable? [3%]$ cd $ printenv PWD /home/bennybeaver $ cd /bin $ printenv PWD /bin
Word Expansions [6%]
Are command words expanded properly? [2%]
$ X=echo $ $X test test $ Y=hello $ Z=world $ $X $Y $Z hello world
$ X=testfile $ DIR=~/$X $ echo $DIR /home/bennybeaver/testfile
Are assignment values expanded properly? [2%]
$ X=1234 $ Y=~/$X $ echo $Y /home/bennybeaver/1234
Are redirection filenames expanded properly? [2%]
$ X=outfile $ echo "hello world" >| ~/$X $ cat /home/bennybeaver/outfile hello world
Redirection [18%]
Note
Each of the tests shown in this section assume that the file(s) being redirected do not initially exist.
>
operator creates a new file, and doesn’t overwrite existing file? [3%]$ echo test > testfile $ cat testfile test $ echo test2 > testfile [[some error message]] $ cat testfile test
>|
operator creates a new file, and does overwrite existing file? [3%]$ echo test >| testfile $ cat testfile test $ echo test2 >| testfile $ cat testfile test2
<
operator works appropriately? [3%]$ sh -c 'echo test > testfile' $ < testfile cat test
<>
operator works appropriately? [3%]$ sh -c 'echo test > testfile' $ <>testfile sh -c 'cat; echo test2 >&0' test $ cat testfile test test2
>>
operator works appropriately? [3%]$ sh -c 'echo hello > testfile' $ cat testfile hello $ >> testfile echo world $ cat testfile hello world
>&
operator works appropriately? [4%]$ 5>&1 sh -c 'echo ERROR! >&5' ERROR!
Multiple redirections work appropriately? [4%]
$ >testfile 5>&1 6>&2 sh -c 'echo HELLO >&5; echo WORLD >&6' WORLD $ cat testfile HELLO
Pipelines [6%]
Pipelines work appropriately? [3%]
$ echo hello world! | sed 's/hello/goodbye/' | cat -v goodbye world!
Pipelines work with redirects? [3%]
$ 5>&1 sh -c 'echo hello world! >&5' | sed 's/hello/goodbye/' | cat -v goodbye world!
Synchronous Commands [15%]
Synchronous commands run in foreground (BigShell waits on them)? [5%]
$ sleep 100 # ...waiting...
Sending stop signal to synchronous commands causes them to be stopped and placed in the background? [5%]
$ sh -c 'sleep 5; killall -SIGSTOP sleep;' & [0] 29347 $ sleep 100 # ... 5 seconds later ... [1] Stopped [0] Done $
Sending kill signal to synchronous commands causes them to exit, and
$?
is updated appropriately? [5%]$ sh -c 'sleep 5; killall -SIGKILL sleep;' & [0] 23959 $ sleep 100 # ... 5 seconds later ... [0] Done $ echo $? 137 # 137 = 128 + 9 (SIGKILL is signal # 9)
Signals [6%]
BigShell ignores the
SIGTSTP
,SIGINT
, andSIGTTOU
signals? [3%]$ kill -s SIGTSTP $$ $ kill -s SIGINT $$ $ kill -s SIGTTOU $$ $
Child processes don’t ignore these signals? [3%]
$ sleep 100 ^C$ # ctrl-C -- SIGINT $
Job Control
Entirely optional–an extra exercise if you want to push your understanding, it won’t be tested in the grading script.