Shell implementation with pipes and I\O redirection in c

时间:2016-12-02 05:16:50

标签: c shell pipe

I'm having a lot of troubles with an assignment for school. I'm supposed to add pipes and I/O redirection to a shell implementation in c. I already got it to work with I/O redirections and the pipes work by themselves but I need to support something like this "sort < file.txt | grep main | cat > output". I have no idea how to make it work with both at the same time. Any help would be gratly appreciated.

int get_args(char* cmdline, char* args[]){
  int i = 0;

  /* if no args */
  if((args[0] = strtok(cmdline, "\n\t ")) == NULL)
    return 0;
  while((args[++i] = strtok(NULL, "\n\t ")) != NULL) {
  if(i >= MAX_ARGS) {
     printf("Too many arguments!\n");
     exit(1);
  }
 }
 /* the last one is always NULL */
 return i;
 }

void pipehandler(char* args[], int nargs){

   int num_cmds = 0;
   char *command[256];
   int filedes[2]; // pos. 0 output, pos. 1 input of the pipe
   int filedes2[2];
   int i = 0;
   //calculate the number of commands
   for(int i = 0; i < nargs; i++){
       if (strcmp(args[i], "|") == 0){
          num_cmds++;
       }
   }
   num_cmds++;
   if(num_cmds <= 1){
      return;
   }
   int j = 0;
   int end = 0;
   pid_t pid;
   while(args[j] != NULL && end != 1){
       int k = 0;

       while (strcmp(args[j],"|") != 0){
           command[k] = args[j];
           j++;
           if (args[j] == NULL){
               // 'end' variable used to keep the program from entering
               // again in the loop when no more arguments are found
               end = 1;
               k++;
               break;
           }
           k++;
       }
       // Last position of the command will be NULL to indicate that
      // it is its end when we pass it to the exec function
      command[k] = NULL;
       j++;

    // Depending on whether we are in an iteration or another, we
    // will set different descriptors for the pipes inputs and
    // output. This way, a pipe will be shared between each two
    // iterations, enabling us to connect the inputs and outputs of
    // the two different commands.
    if (i % 2 != 0){
        pipe(filedes); // for odd i
    }else{
        pipe(filedes2); // for even i
    }
    pid=fork();

    if(pid==-1){
        if (i != num_cmds - 1){
            if (i % 2 != 0){
                close(filedes[1]); // for odd i
            }else{
                close(filedes2[1]); // for even i
            }
        }
        printf("Child process could not be created\n");
        return;
    }
    if(pid==0){
        // If we are in the first command
        if (i == 0){
            dup2(filedes2[1], STDOUT_FILENO);
        }
        // If we are in the last command, depending on whether it
        // is placed in an odd or even position, we will replace
        // the standard input for one pipe or another. The standard
        // output will be untouched because we want to see the
        // output in the terminal
        else if (i == num_cmds - 1){
            if (num_cmds % 2 != 0){ // for odd number of commands
                dup2(filedes[0],STDIN_FILENO);
            }else{ // for even number of commands
                dup2(filedes2[0],STDIN_FILENO);
            }
        // If we are in a command that is in the middle, we will
        // have to use two pipes, one for input and another for
        // output. The position is also important in order to choose
        // which file descriptor corresponds to each input/output
        }else{ // for odd i
            if (i % 2 != 0){
                dup2(filedes2[0],STDIN_FILENO);
                dup2(filedes[1],STDOUT_FILENO);
            }else{ // for even i
                dup2(filedes[0],STDIN_FILENO);
                dup2(filedes2[1],STDOUT_FILENO);
            }
        }
        execvp(command[0],command);
    }

    // CLOSING DESCRIPTORS ON PARENT
    if (i == 0){
        close(filedes2[1]);
    }
    else if (i == num_cmds - 1){
        if (num_cmds % 2 != 0){
            close(filedes[0]);
        }else{
            close(filedes2[0]);
        }
    }else{
        if (i % 2 != 0){
            close(filedes2[0]);
            close(filedes[1]);
        }else{
            close(filedes[0]);
            close(filedes2[1]);
        }
    }

    waitpid(pid,NULL,0);

    i++;
  }



 }

 int searchIO(char* args[], int *nargs, char** input_file, char** output_file, int* in, int* out, int* ap){
 int ioRedirection = -1;
 //search through the array args
 printf("searching for io redirections\n");
 for(int i = 0; i < *nargs; i++){
    if(strcmp(args[i], "<") == 0){
        //if you find < then you need to redirect the stdin to be from a file
        //if you are here then the input file is in args[i+1]
        *input_file = args[i+1];
        printf("this is the input file %s \n", *input_file);
        //we need to remove the < and input_file from args[]
        int j;
        for(j = i; j < *nargs; j++){
            //move what is in args[j+2] to args[j];
            args[j] = args[j+2];
        }
        //then we need to execute the comand
        *nargs = *nargs-2;
        *in = 1;
        i = 0;
        ioRedirection = 1;
    }else if (strcmp(args[i], ">") == 0){
        //if you are here then the output file is in args[i+1]
        //so aparently execute what is in args[0] to args[i]
        *output_file = args[i+1];
        //we need to remove the > and output_file from args[]
        int j;
        for(j = i; j < *nargs; j++){
            //move what is in args[j+2] to args[j];
            if(j < *nargs){
                args[j] = args[j+2];
            }
        }
        *nargs = *nargs-2;
        *out = 1;
        i = 0;
        ioRedirection = 1;
    }else if (strcmp(args[i], ">>") == 0){
        //if you are here then the output file is in args[i+1]
        //so aparently execute what is in args[0] to args[i]
        *output_file = args[i+1];
        //we need to remove the > and output_file from args[]
        int j;
        for(j = i; j < *nargs; j++){
            //move what is in args[j+2] to args[j];
            args[j] = args[j+2];
        }
        *nargs = *nargs-2;
        *out = 1;
        *ap = 1;
        i = 0;
        ioRedirection = 1;
    }

  }
  return -1;

 }

 void execute(char* cmdline){
 int pid, async;
 char* args[MAX_ARGS];
 char *input_file;
 char *output_file;
 int _in, _out, _ap;
 _in = 0;
 _out = 0;
 _ap = 0;
 int nargs = get_args(cmdline, args);
 pipehandler(args, nargs);
 searchIO(args, &nargs, &input_file, &output_file, &_in, &_out, &_ap);
 if(nargs <= 0) return;
 if(!strcmp(args[0], "quit") || !strcmp(args[0], "exit")) {
        exit(0);
 }
 if(!strcmp(args[nargs-1], "&")) { async = 1; args[--nargs] = 0; }
 else async = 0;

 pid = fork();
 if(pid == 0) { /* child process */
   if(_in == 1){
      int input = open(input_file, O_RDONLY);
      dup2(input, STDIN_FILENO);
      close(input);
  }
  if(_out == 1){
      int output;
      if(_ap){
          output = open(output_file, O_APPEND | O_CREAT);
      }else{
          output = open(output_file, O_CREAT | O_WRONLY, 0666);
      }

      dup2(output, STDOUT_FILENO);
      close(output);
  }
 execvp(args[0], args);
 /* return only when exec fails */
  perror("exec failed");
  exit(-1);
  } else if(pid > 0) { /* parent process */
 if(!async) waitpid(pid, NULL, 0);
  else printf("this is an async call\n");
 } else { /* error occurred */
 perror("fork failed");
 exit(1);
 }
 }
 int main (int argc, char* argv [])
 {
 char cmdline[BUFSIZ];

  for(;;) {
  printf("$ ");
  if(fgets(cmdline, BUFSIZ, stdin) == NULL) {
    perror("fgets failed");
    exit(1);
  }
  execute(cmdline) ;
 }
 return 0;
 }

1 个答案:

答案 0 :(得分:0)

  

如果正在使用管道,则唯一可以使用I / O重定向的地方是   第一个命令和最后一个命令但不在中间右边?

只有重定向 stdin / stdout 时才会这样。

  

I / O重定向和管道自己工作,但......我不知道   如何同时使用它们。

重组您的实施。如果你打电话

,它就无法工作
 pipehandler(args, nargs);
 searchIO(args, &nargs, &input_file, &output_file, &_in, &_out, &_ap);

在彼此之后,这也导致错误,即管道的第一个命令被执行两次(首先来自pipehandler,然后是来自searchIO的错误参数)。相反,您可以让mainpipehandler内调用execute然后pipehandler每个单独的命令(即使只有一个也没有管道)。可以很容易地诊断出冲突的I / O重定向和管道。