Making Our Own Shell
In this part, you are allowed to use functions from the C standard library, such as printf, malloc, free, strlen, strtok, and strerror.
Writing vbsh (Very Basic Shell)
A shell is an interactive command-line interpreter that runs programs on behalf of the user. A shell repeatedly prints a prompt (in our case vbsh>), waits for a command line on standard input, and then carries out some action, as directed by the contents of the command line.
The command line is a sequence of ASCII text words delimited by whitespace. The first word in the command line is either the name of a built-in command or the pathname of an executable file. The remaining words are command-line arguments.
If the first word is a built-in command, the shell immediately executes the command in the current process. Otherwise, the word is assumed to be the pathname of an executable program, and the shell forks a child process, then loads and runs the specified program in the context of the child.
Typing the command line
vbsh> /bin/ls -l -d
runs the ls program in the foreground.
C Strings
Remember also that C-style strings are NUL-terminated, and in memory look like this.
H |
e |
l |
l |
o |
\0 |
| 0 | 1 | 2 | 3 | 4 | 5 |
You can find more about C strings in the C Strings Help Page.
Provided Code
We have already provided some code in vbsh.c to get you started. As provided, the shell supports
- Running the (built-in)
exitcommand to exit the shell (with no status information). - Running external commands.
- Scans the provided command line to break it into words so that each word can be passed as an argument to the program being run.
- Uses the
execvpfunction to run the program.execvpis a library wrapper around theexecvesystem call that searches the directories in thePATHenvironment variable for the program to run. Thus we can actually just typelsinstead of/bin/lsto run thelsprogram.
Your Task
You will need to add the following features:
- Running the following built-in commands:
chdir.- Ideally, write a generic mechanism to support built-in commands. You can look at the code in
menu.cfrom OS/161 for inspiration. If you use this approach, it would be easy to makecdan alias forchdir(not required, but it would be a nice touch).
- Ideally, write a generic mechanism to support built-in commands. You can look at the code in
- Reporting the exit status of a command if it is not zero.
- Simple background execution (described below).
- Providing I/O redirection for external commands. For our purposes, each I/O redirection specification is a single argument of the form
<fileor>file(i.e., no spaces), where each kind of I/O redirection is specified, at most, once.
Status Reporting
If an external program exists with non-zero exit status, your shell should display that status in the form Exit code: n, where n is the exit status.
Background Execution
A user can ask a program to be run in the background by appending a trailing & as the final word on the command line. If the user specifies no input redirection, your shell should close standard input for the command. When a background task is initiated, the shell should print PID p started., where p is the process id, and then continue to read further commands from the user as normal. Just before printing the prompt, the shell should check to see if any background tasks have completed and, if so, print PID p completed, exit code n. for each one.
Sample Session
vbsh> /bin/true
vbsh> /bin/false
Exit code: 1
vbsh> mkdir testdir
vbsh> chdir testdir
vbsh> ls -l
total 0
vbsh> echo Wow >wow.txt
vbsh> ls -l
total 4
-rw-r--r-- 1 melissa melissa 4 Sep 2 15:46 wow.txt
vbsh> cat <wow.txt
Wow
vbsh> sleep 5 &
PID 576474 started.
vbsh> echo lets wait
lets wait
vbsh> echo is it done?
is it done?
PID 576474 completed, exit code 0.
Notice that while chdir is a built-in command, mkdir is an external program. Some shells include their own built-in versions of commands provided by the OS (which might behave differently than the OS's version).
See man builtin for some examples.
You do not need to support:
- Quoting for arguments to external commands
- Glob expansion (e.g.,
ls *.c) - Spaces after redirection symbols (e.g.,
> fileis not supported) - No space between the command and the
&for background execution (e.g.,sleep 5&is not supported) - Pipes
- Signals
- Line editing and command history
- Job control (e.g., killing background tasks)
- Environment variables (e.g.,
echo $PATH) - Command substitution (e.g.,
echo $(ls)) - Arguments to the shell command itself (it needs none)
If you would like to go further with your shell, speak to me about extra-credit possibilities.
Finally, remember the course policies on collaboration and academic integrity. You may discuss the assignment with others, but you must write your own code. As always, you may not use a large language model (e.g., ChatGPT or GitHub Copilot) to generate code for you.
If you do look at other code sources (e.g., looking at OS/161 code or Stack Overflow), you should not copy them verbatim into your code. Instead, you should understand the code you are looking at and then write your own code from scratch. If you do look at other code sources, you must wait a few minutes after looking at it before you work on your own code to avoid any risk of accidentally using your short-term memory and thus repeating code or concepts by rote.
(When logged in, completion status appears here.)
Background on Command Line Arguments
When a program is run from the command line, the shell passes the program a list of arguments. By convention, the shell ensures that when the program begins executing its main routine,
the
argcparameter is a count of the number of arguments, andargv(which can also equivalently be declared asconst char *argv[]) is an array of pointers to the arguments. The first argument,argv[0], is the name of the program being run. The last argument,argv[argc], is a null pointer to indicate the end of the arguments.Thus, in this case
argcandargvarguments have the following values:argcargv[0]/bin/lsargv[1]-largv[2]-dargv[3]