Please submit a single tar file.
A shell is a command interpreter. It uses system calls to start and stop programs and to direct their output into files. A single OS can have any number of shells; each user that can log into a system via telnet or ssh generally has one default shell. In Linux, FreeBSD and MacOSX, common examples are bash, csh and ksh. The DOS prompt and the Windows command-line interface are other examples.
Your assignment is to write your own shell to replace the standard shell in your host system. This assignment will let you get familiar with C programming in Unix-like system, as well as the basic concepts and structure of this important user interface of Unix-like systems.
This is a regular C program, not a kernel modification. It should be runnable and testable in a Unix-like system, i.e., Linux in this class.
The shell consists of a main()
function that
continuously reads lines from stdin, parses the line into
space-separated words and executes the command, giving user feedback.
The parser may use scanf()
, strtok()
or other
suitable C library functions.
The shell or Command Line Interpreter (CLI) that is your environment when you logon to Unix-like system, a completely compatible version and subset of the Berkeley UNIX C shell, csh. It is a command language interpreter usable both as an interactive login shell and a shell script command processor.
Your shell should be able to accept commands, analyze them, interpret and execute a set of shell-defined commands, as described below.
Your shell should give reasonable output and feedback to the user, such as producing an error message when an executable cannot be found or cannot be accessed.
For all programs except ls and cd, use the
exec() system call, or one of its variants, such as
execve()
. Use of a function that executes a shell process,
such as system()
or popen()
, is not permitted for
this assignment, including for cd and ls. You
cannot use a shell process such as exec("/bin/sh -c
command")
.
Useful system calls include exec()
, fork()
,
readdir()
, setenv()
, getenv()
.
For details, see the unix man
pages of these function calls.
You should use the getopt library call to process command line arguments.
The cd command in standard shell in to change the directory. Your shell should be able to accept the command and the following arguments:
cd | Back to the home directory of current user |
cd .. | Back to the parent directory |
cd path | change to the specified path directory |
The getcwd
and chdir()
functions are relevant
here.
The ls command in a standard shell lists files in the current working directory. You only have to implement the following subset of its functionality. You should model the output on that of the standard Linux shell commands. (Note that Linux uses /bin/ls, rather than a shell built-in function.)
ls | list all regular files |
ls -a | list all files in this directory, including hidden files (those starting with a .) and system files (devices, links) |
ls -l | list files with details |
Your shell should be able to interprete the compound parameters, like ls -al.
Use readdir()
for implementing ls. You do not
have to support putting ls in the background.
/home/foo/bar $
There are three file descriptors, stdin, stdout and stderr (std = standard). For this assignment, you will only implement output redirection, using the > symbol, so that stdout and stderr are redirected to a file instead of the terminal.
Here, a file called 'ls-l.txt' will be created and it will contain what you would see on the screen if you type the command ls -l and execute it.
ls -l > ls-l.txt
You will find the dup2()
system call of interest. The
basic outline is
fd = open new descriptor for output file new descriptor = dup2(fd, 1); close(fd);
An important function of the shell is running executable files. Your shell should be able to run the executable file in the current working directory as well as those in the system working path ($PATH).
The fork()
system call allows you to create another
process and the run, using exec()
, the command entered by
the user on the command line. Depending on whether you wait for the
completion, the process either runs in the foreground or background.
Normally, the user runs the program in the foreground, which means it will block the shell to run the program. The shell will not be able to accept another command until the running program has finished.
./program [parameters]
Unix allows you to wait for a process using the wait()
and waitpid()
system calls.
If a user wants to execute a second command while another program is running, the first program needs to run in the background.
You may have to create data structures like an array or linked list to store information about the processes started by your shell, so that the jobs and fg commands can obtain the correct process number.
On your shell command line, a trailing & indicates that the shell will be running this program in the background and assign a numeric job ID as feedback to user.
./program [parameters] &
[1] program1 [2] pragram2
Your shell should also be able to bring background jobs to the foreground status by fg [id]. The command will bring the background job with id to the foreground, and at meantime, your shell should be block and cannot take another command.