Hi all,
I was asked to write a shell program in c in unix....basically what i would like to include are the use of dup2 to implement redirection ( < or >) and the use of dup2() and the pipe() system call to implement the '|' symbol on the command line...and so below is the code...basically it works for those command like
ls -l | grep test , ls -l > file but it doesn't work when i enter ls -l | grep test > file2 as the output of ls -l | grep test doesn't written into file2... i added some of the printf at some of the place to check...
is there anyone can help me to figure out what's the problem...thanks in advance!
any help will be greatly appreciated:)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define SIZE 80
/********************** Parse the Command Line by | and store in temp[i] ***********************************/
void ParseInput(char *buf, char **temp, int *count)
{
int i = 0;
int nos, s, pos;
char *command;
char space[] = " \t"; // remove space or tab before the command, if any
char nl[] = "\t\n"; // remove new lines or tab, if any
command = strtok(buf,"|"); // tokenize the command line by |
while(command != NULL)
{
nos = strspn (command, space); // return the number of spaces or tab before the command
for (s = 0; s < nos; s++)
++command; // increment the pointer of command to remove the spaces or tabs, if any
if (pos = strcspn (command, nl))// find if there's any new line or tab after the command
command[pos] = '\0'; // replace it with a null, if any
for (;;) // make a loop
{
int n = strlen (command) - 1; // last character position in command
if (command[n] == ' ') // if there's a space
command[n] = '\0'; // makes the string shorter
else
break; // out of the loop, if any non-white space is found
}
temp [i] = (char *)command; // store each commands into the array
command = strtok(NULL,"|"); // tokenize the command line by |
i++; // Increment i
}
*count = i; // Count equals to i
}
/******************** Parse the Command into arguments and exec the arguments **************************/
void execute(char *temp, int r, int w)
{
pid_t pid;
char *argument;
char *argv[80]={0};
char *input = "<";
char *output = ">";
int *array2[SIZE]; // create an array of int *
int k = 0;
int fds[2];
pid = fork();
if (pid == 0) /* This is the child process */
{
memset(argv,0,sizeof(argv)); // Remove the elements in argv array
argument = strtok((char *)temp," \t\n"); // tokenize the command into arguments
while ((char *)argument != NULL)
{
if (strcmp((char *)argument,input) == 0) // Check if the '<' exists in the argv array
{
argument = strtok(NULL," \t\n"); // tokenize the command into arguments
r = CreateInputRedirectFile(argument); // Return a file descriptor from CreateInputRedirectFile
}
else if (strcmp((char *)argument,output) == 0) // Check if the '>' exist in the argv array
{
argument = strtok(NULL," \t\n"); // tokenize the command into arguments
w = CreateOutputRedirectFile(argument); // Return a file descriptor from CreateOutputRedirectFile
printf("write to file\n");
}
else
{
printf("%s\n",argument);
argv[k] = argument; // store each arguments into the array
k++; // Increment k
}
argument = strtok(NULL," \t\n"); // tokenize the command into arguments
}
printf("%s %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3], argv[4]);
if (r != -1) // if fds[0] is valid
{
dup2(r,0); // Reassign stdin to r = fds[0] end of pipe
}
if (w != -1) // if fds[1] is valid
{
dup2(w,1); // Reassign stdout to w = fds[1] end of pipe
}
close(r); // Not going to write in this child process, so we can close this end of pipe
close(w); // Not going to write in this child process, so we can close this end of pipe
execvp(argv[0],argv); // execute the given command
write(1,"exec fail\n",10); // print this message if fails to execvp
exit(1);
}
else if (pid < 0) // error
{
printf("fork failed");
exit(1);
}
}
/**************** Create a Pipe function if there exists a pipe in the command line ****************************/
int *CreatePipe()
{
int *fds = (int *)calloc(2, sizeof(int)); // Dynamically allocated memory
pipe(fds); // Create a pipe
return (int *)fds; // Return the two file descriptors
}
/**************** Create a Input Redirect function if there exists a < in the command line **********************/
int CreateInputRedirectFile(char *argument)
{
int fdi; // file descriptor
fdi = open(argument, O_RDONLY); // Open the input files
if (fdi < 0)
printf("Open read fail"); // Error
return fdi; // Return a file descriptor
}
/**************** Create a Output Redirect function if there exists a > in the command line *********************/
int CreateOutputRedirectFile(char *argument)
{
int fdo; // file descriptor
mode_t mode = S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR; // permission mode for the created file
fdo = open(argument, O_CREAT | O_RDWR | O_TRUNC, mode); // Open the output files
if (fdo < 0)
printf("Open file fail"); // Error
printf("write successful\n");
return fdo; // Return a file descriptor
}
/********* Main function that pass a particular parameters from ParseInput function to Execute function ****************/
int main()
{
int j = 0;
char buf[80]; // command line
char *temp[80]={0}; // an array to store the commands
int count;
pid_t pid;
int read_fds, write_fds;
int *array[SIZE]; // create an array of int *
int invalid = -1;
printf("prompt-> ");
while (strcmp(fgets((char *)buf, 80, stdin), "exit\n") !=0) // user command line
{
if(strcmp(buf, "\n")!=0) // user command line
{
memset(temp,0,sizeof(temp)); // Remove the elements in temp array
memset(array,0,sizeof(array)); // Remove the elements in array
int count = 0; // Initialize count to 0
int j = 0; // Initialize j to 0
ParseInput(buf, temp, &count); // Parse the user input line
if (count == 1) // If count is 1 (only one command)
{
execute(temp[j], invalid, invalid); // call execute func
}
if (count > 1) // for more than one command (with pipes)
{
array[j] = CreatePipe(); // store the return value of CreatePipe to array[j]
write_fds = array[0][1]; // copy the write end of the pipe to write_fds
execute(temp[j], invalid, write_fds); // call the execute func and pass the end of pipe
j++; // increment of j
for(j = 1; j < count - 1; j++) // for each command
{
array[j] = CreatePipe(); // create another pipe
read_fds = array[j-1][0]; // copy the read end of previous pipe
write_fds = array[j][1]; // copy the write end of current pipe
execute(temp[j], read_fds, write_fds); // call the execute func
}
if (j == count - 1) // for the last command
{
read_fds = array[j-1][0]; // copy the read end of previous pipe
execute(temp[j], read_fds, invalid); // call the execute func
}
}
}
else
printf("prompt-> ");
memset(buf,0,sizeof(buf)); // Remove all the elements in buf
}
return 0;
}
prompt-> ls -l | grep test > file
ls
-l
ls -l (null) (null) (null)
grep
test
write successful
write to file
grep test (null) (null) (null)
prompt-> ls -l file
ls
-l
file
ls -l file (null) (null)
-rw-r----- 1 cpfoo ugrad 0 2011-04-23 09:53 file <-- nothing is written 0 bytes??
prompt-> ls -l > file <-- without | in btw commands
ls
-l
write successful
write to file
ls -l (null) (null) (null)
prompt-> ls -l file
ls
-l
file
ls -l file (null) (null)
-rw-r----- 1 cpfoo ugrad 519 2011-04-23 09:53 file <--ls -l is written into file with 519 bytes and i checked using cat file, everything in ls -l was written too.....