Mar. 1, 2023
For CS344, I wrote a Unix shell program for a mid-term assignment. The shell is written in C and can be compiled with GCC. Per the assignment requirements, the program needed to do the following:
$$
, $?
, $!
, ~
)exit
and cd
EXEC(3)
function<
and >
&
operator to run background commandsSIGINT
and SIGSTP
signalsAs a relative newcomer to programming in C, this project was a big challenge. Success in this project really demanded that the requirements be broken down into manageable sub-tasks. The order of building and integrating each of the component parts became important. We had two weeks (though, with overlapping deadlines for two other projects) to complete the assignment and I started as early as it was released and worked every day until the day before the deadline to complete it. Below, I discuss the major component parts of the project in the general order I constructed them to complete the assignment. The project can be viewed on GitHub here: https://github.com/bushjam1/cs344_smallsh
Our class was required to build the project using SSH on a RHEL server maintained by the college. Our instructor asked that we use Neovim, for which we were provided a Bash script that implemented basic features like autocomplete. We were required to compile the program with GCC and allowed to use GDB for debugging. A script was also provided that functioned as a grading script. The score we recieved on the grading script was the grade we received for the project. The grading script was useful for taking a sort of test-driven development approch, which most of the class used.
Before printing an input prompt to the command line, our program needed to check
for any background processes in the same process group ID as the program and print
an error message to stderr
for exited or signaled processes.
For the input prompt, we were required to do parameter expansion and print the PS1
parameter $
for an input prompt. After printing the input prompt, the program is
required to read from stdin
.
Once input is obtained from stdin
, I performed word splitting / tokenization
with a function, split_words()
, that tokenized the input string in a loop with
inbuilt method, strtok()
. Each actionable, tokenized word is placed in a word
array and then passed to the function parse_words()
to determine the series of
commands and options to run.
As mentioned above, string expansion is performed by the expand_word()
function,
which essentially takes several key words and converts them into more meaningful strings for
execution of various tasks. These include the tilde, ~
for the home directory,
$$
for the foreground process ID, $?
for the last foreground process
exit status, and $!
for the PID of the most recent background process.
The relevant expansion is then returned as a character array to pass to parse_words()
.
The parsing logic implemented in parse_words()
is the final step before specific
commands and options are run. It serves several purposes. First, it detects comments indicated by
the hashmark #
. It also detects whether a process is indicated to run as a background
process by the presence of an amperstand &
at the end of a command. It will then detect command
words and run them as necessary, or return to the command prompt if no commands are detected.
The execution function, execute_commands()
, performs the bulk of logical operations in
SMALLSH program. It first checks for the presence of any invocation of cd
or exit
and
will run those with the cd_smallsh()
or exit_smallsh()
functions. If neither cd or exit
are invoked, execute_commands()
will then pass the word/commands to run as a new child process by using FORK(2)
.
Description
Description