c - 在 c 中使用管道和 I\O 重定向的 Shell 实现

标签 c shell pipe

我在学校作业上遇到了很多麻烦。我应该将管道和 I/O 重定向添加到 c 中的 shell 实现中。我已经让它与 I/O 重定向一起工作并且管道自己工作但我需要支持这样的东西“sort < file.txt | grep main | cat > output”。我不知道如何让它同时与两者一起工作。任何帮助将不胜感激。

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;
 }

最佳答案

if pipes are being used the only place to use I/O redirections is in the first command and in the last one but not in the middle right?

只有当 stdin/stdout 被重定向时,这才是正确的。

I/O redirections and the pipes work by themselves but … I have no idea how to make it work with both at the same time.

重组您的实现。如果你调用它就不能工作

 pipehandler(args, nargs);
 searchIO(args, &nargs, &input_file, &output_file, &_in, &_out, &_ap);

一个接一个,这也导致管道的第一个命令被执行两次的错误(首先来自 pipehandler,然后来自 searchIO,参数错误)。相反,您可以让 main 调用 pipehandler,然后从 pipehandler执行每个单独的命令(即使有只有一个,没有管道)。可以轻松诊断冲突的 I/O 重定向和管道。

关于c - 在 c 中使用管道和 I\O 重定向的 Shell 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40925272/

相关文章:

c - 错误的文件描述符

batch-file - 如何在管道中涉及的 FOR 循环中设置变量?

c - printf 和++ 运算符

c - yocto 项目中的 do_rootfs 函数失败

linux - 使用 shell 在文本文件中查找重复项

linux - 如何在shell中访问以句点 '.'开头的文件

c++ - 将列表分成相等的部分?

c - 为什么返回类型的存在使得不需要对参数进行前向声明?

c - 以编程方式访问shell扩展,替换和单词拆分

linux - 当管道的第一个命令不输出时如何不输出任何内容