Shell Program - Write, compile, and run a C program for a custom-shell.
Part 1: Command Parsing (and print its components correctly).
- Your shell should be able to parse the command from user.
- Assume my home directory is: /home/richard or you may use.
Examples (You may create your own output format or template to show the command(s) being parsed.)
1. One command with arguments and options.
For example, ls –la /home/richard
Command: ls
Options: -la
Arguments: /home/richard
2. Two commands with 1 pipe.
For example, ls –la /home/richard
Command: la
Options: -la
Arguments: /home/richard
For example, ls | wc –l should be parsed and display
Command: ls
Pipe
Command: wc –l
3. Two commands with semicolon. For example, ls ; date
4. One command with IO redirection symbol (<, >, >>)
For example, ls < junk.txt > output.txt
Command: ls
File Redirection: <
File: junk.txt
File Redirection: >
File: output.txt
5. Three commands or more commands with pipe (and with other symbols). For example
ls –l | grep *.txt | wc –l
ls –l | grep *.txt | wc –l | ls > junk.txt
Part 2: File Redirection. >, <, >> with a single command using fork/exec, dup/dup2, and/or pipe
For example,
ls > output.txt
ls >> output.txt
sort < output.txt
You may use the code segment of the following program(s) for this part.
// For Part2 – simple command shell (for a single command).
// Note: For Part1 you may begin with the following simple shell program.
/*
* World's simplest shell. Loops, reads input and tries to execute it.
* Note: no tokenization, can be ^C'd, but does look at PATH not sorted
* ./simple-shell
* $$ ls
* $$ ^C
*/
#include
#include
#include
#include
#include
#include
#include
#include
char *
getinput(char *buffer, size_t buflen) {
printf("$$ ");
return fgets(buffer, buflen, stdin);
}
int
main(int argc, char **argv) {
char buf[1024];
pid_t pid;
int status;
while (getinput(buf, sizeof(buf))) {
// parent is waiting
buf[strlen(buf) - 1] = '\0';
if((pid=fork()) == -1) {
fprintf(stderr, "shell: can't fork: %s\n",
continue;
} else if (pid == 0) {
/* child to run the command */
/* your code to parse the command & set any file redirection as needed */
execlp(buf, buf, (char *)0);
fprintf(stderr, "shell: couldn't exec %s: %s\n", buf,
exit(EX_DATAERR);
}
if ((pid=waitpid(pid, &status, 0)) < 0)
fprintf(stderr, "shell: waitpid error: %s\n",
strerror(errno));
strerror(errno));
strerror(errno));
}
exit(EX_OK);
}
/* program code 2 - a sample code for output redirection */
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char **argv)
{
int pid, status;
int fdout; /* file descriptor for output */
char *command[] = { "ls", "-l", ">", "output.txt" };
if ((fdout = open(command[3], O_CREAT|O_TRUNC|O_WRONLY, 0644)) < 0) {
}
printf("writing output of the command %s to \"%s\"\n", command[0], command[3]);
dup2(fdout, 1);
execlp(command[0], command[0], command[1], (char *)0);
perror(command[0]); /* execvp failed */
exit(1);
}
perror(command[3]); /* open failed */
exit(1);
Part 3: Implement a shell (using fork, pipe, dup/dup2, exec) for a single command (Part 1), two commands, and then
(1) Handle two commands with fork, pipe, dup/dup2.
ls -l | wc -l
Given a command from user, it forks a child process to execute the command while parent waits. When it is done, parent process wakes up and prints prompt for next command.
Syntax: command [options] | command [options]
Test line: ls -sF | wc -l
You may use the code segment (as an example) from the following sample program.
/* implementing "/bin/ps -ef | /bin/more" */
#include
#include
#include
#include
int main()
{
int fds[2];
int child[2];
char *argv[3];
pipe(fds);
if (fork()== 0)
{
close(fds[1]);
close(STDIN_FILENO); dup(fds[0]);
/* redirect standard input to fds[0] */
argv[0] = "/bin/more";
argv[1] = NULL;
execv(argv[0], argv);
exit(-1);
}
if (fork() == 0)
{
close(fds[0]);
close(STDOUT_FILENO); dup(fds[1]);
argv[0] = "/bin/ps";
argv[1] = "-ef"; argv[2] = NULL;
execv(argv[0], argv);
exit(-2);
}
close(fds[0]); close(fds[1]);
wait(child);
wait(child+1);
return 0;
}
multiple commands
ls -l | wc -l
(2) Handle multiple-commands with pipe
e.g., ls -l | sort | head
(3) Handle multiple-commands with pipe, and file redirection
e.g., ls -l | sort | head > output.txt
e.g., cat < output.txt | grep ".c" | head > list.txt
Part 4: Send a command via socket program to be run a "ls -l" command, and echo its result back to the sender.
Implement a server to listen port (nnnnn) for any incoming ls command to be run and to output its result back to the sender. The client sends a command (e.g., ls –l) to the server and receive the result to be output to the console. You may modify the sample programs (for echo server/client or time server/client) for your base code to be modified.
Warning and Caution. Your server's listening port must be free (not taken by someone else) and pick one between 40000 and 64000. If it is taken, your server should use another port available, to be connected by your client. Do not use any port being used by someone else for server to bind and for client to connect (to someone's server, not yours).