在C中创建一个处理重定向和管道的shell

时间:2017-04-23 07:06:00

标签: c shell

以下函数成功执行任何不包含管道的命令,因此不必担心奇怪的功能。这些工作。我遇到的问题是每当我执行以下任何命令时:

cat file.txt | grep string

命令已成功执行,但它仍然处于空闲状态,因此不知何故它被卡住了,没有其他命令可以执行。为什么会这样?我认为这与我使用pipe,dup和fork的方式有关,所以试着从这些函数中解决问题。我知道你可能会争辩说这段代码不能用于管道的其他命令,但我只想让这个特定的例子工作,并且这样做我只是在第一次迭代中将STDIN重定向到打开的文件。 / p>

    int myshell_execute(struct processNode* list, int a)
    {

      struct processNode* myList = list; // next node to be handled
      int pipefd[2];
      int in=0;
      if (pipe(pipefd) == -1) {
            perror("pipe");
            myshell_exit(-1);
        }

      while(myList != NULL)
      {

      char* program = myList->program;  // Get the program to be executed
      char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
      char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
      int *status;
      int* stdout;
      int stdout_num = 1;
      stdout = &stdout_num;
      int fileDescriptor;
      pid_t pid;

        if(strcmp(program,"cd") == 0)
        {
          return myshell_cd(program_args);
        }
        else if (strcmp(program,"exit") == 0)
        {
          return myshell_exit(0);
        }


      pid = fork();

      if(pid == 0)
      {  

        if(in == 1)
        {
         close(pipefd[1]);
         dup2(pipefd[0],0);
         close(pipefd[0]);
        }

        if(sizeOfLine(redirection_string) != 0)
        {
        redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
        }

        if(*stdout == 1 && myList->next !=NULL)
        { 
          close(pipefd[0]);
          dup2(pipefd[1],STDOUT_FILENO); // with this
          close(pipefd[1]);
        }
        if(execvp(program,program_args) !=-1)
        {
          perror("myshell:");
          myshell_exit(-1);
        }
        else{
          myshell_exit(0);
        }
      }
      else if (pid <0)
        {
          perror("myshell: ");
          myshell_exit(-1);
       }
      else
       {
         wait(status);  

       }
       in = 1;
      myList = myList->next;

      }
    }

新解决方案:

int helper_execute(int in, int out, char* program, char ** program_args, char *redirection)
    {
      pid_t pid;

      if ((pid = fork ()) == 0)
        {
          if (in != 0)
            {
              dup2 (in, 0);
              close (in);
            }

          if (out != 1)
            {
              dup2 (out, 1);
              close (out);
            }

          redirectionHandler(redirection);
          return execvp (program, program_args);
        }

      return pid;
    }


        int myshell_execute(struct processNode* list, int a)
        {
          int i;
          pid_t pid;
          int in, fd [2];
          struct processNode*new_list = list;
          char ** newProgram = new_list->program_arguments;
          char ** redirection = new_list->redirection;
          char * program = new_list->program;

          /* The first process should get its input from the original file descriptor 0.  */
          in = 0;

          /* Note the loop bound, we spawn here all, but the last stage of the pipeline.  */
          while(new_list->next != NULL)
            {
              pipe (fd);
              /* f [1] is the write end of the pipe, we carry `in` from the prev iteration.  */
              helper_execute (in, fd [1],program,newProgram,redirection);

              /* No need for the write end of the pipe, the child will write here.  */
              close (fd [1]);

              /* Keep the read end of the pipe, the next child will read from there.  */
              in = fd [0];

              new_list = new_list->next;
            }

          /* Last stage of the pipeline - set stdin be the read end of the previous pipe
             and output to the original file descriptor 1. */  
          if (in != 0)
            dup2 (in, 0);

          /* Execute the last stage with the current process. */
          char* lastProgram = new_list->program;
          char ** lastRedirection = new_list->redirection;
          char * lastPrArguments = new_list->program_arguments;
          redirectionHandler(redirection);
          return execvp (lastProgram, lastPrArguments);
        }

        int main() {
          int i=0;
          char **input;
          struct processNode* list;
          int tracker = 0;

          while ((input = getline()) != EOF) {  
              list = create_list(input);
              myshell_execute(list,0);
          }

          return 0;
        }

此解决方案的唯一问题是,只要执行一个命令,main就会立即检测到文件的结尾,因此它会退出shell。

1 个答案:

答案 0 :(得分:0)

这是因为

  1. 您的父进程也会打开管道,
  2. 在你分叉后拨打wait。如果您的第一个进程(cat)填充其输出管道,并且没有任何进程可用于使用管道的读取结束,则该进程将永久停止。
  3. 看起来像这样:

    #define MAX_PIPE_LEN 256
    int myshell_execute(struct processNode* list, int a)
    {
        /* fd0 are the input and output descriptor for this command. fd1
           are the input and output descriptor for the next command in the
           pipeline. */
        int fd0[2] = { STDIN_FILENO, STDOUT_FILENO },
            fd1[2] = { -1, -1 };
        pid_t pids[MAX_PIPE_LEN] = { 0 };
        int pipe_len;
        struct processNode* myList; // next node to be handled
        int status;
        int failed = 0;
        for (pipe_len = 0, myList = list;
             pipe_len < MAX_PIPE_LEN && myList != NULL;
             pipe_len++, myList = myList->next) {
            char* program = myList->program;  // Get the program to be executed
            char ** program_args = myList->program_arguments; // get the programs and arguments to be executed
            char ** redirection_string = myList->redirection; //get the part of the command that contains redirection
            if(strcmp(program,"cd") == 0) {
                return myshell_cd(program_args);
            }
            else if (strcmp(program,"exit") == 0) {
                return myshell_exit(0);
            }
            if (myList->next != NULL) {
                /* The output of this command is piped into the next one */
                int pipefd[2];
                if (pipe(pipefd) == -1) {
                    perror("pipe failed");
                    failed = 1;
                    break;
                }
                fd1[0] = pipefd[0];
                fd1[1] = fd0[1];
                fd0[1] = pipefd[1];
            }
            pids[pipe_len] = fork();
            if (pids[pipe_len] < 0) {
                perror("error: fork failed");
                failed = 1;
                break;
            }
            if (pids[pipe_len] == 0) {  
                if (fd0[0] != STDIN_FILENO) {
                    if (dup2(fd0[0], STDIN_FILENO) == -1) {
                        perror("error: dup2 input failed");
                        abort();
                    }
                    close(fd0[0]);
                }
                if (fd0[1] != STDOUT_FILENO) {
                    if (dup2(fd0[1], STDOUT_FILENO) == -1) {
                        perror("error: dup2 outut failed");
                        abort();
                    }
                    close(fd0[1]);
                }
                if (fd1[0] >= 0) {
                    close(fd1[0]);
                }
                if (fd1[1] >= 0) {
                    close(fd1[1]);
                }
                if(sizeOfLine(redirection_string) != 0) {
                    redirectionHandler(redirection_string,stdout); // This works. This just handles redirection properly
                }
                execvp(program, program_args);
                perror("error: execvp failed");
                abort();
            }
            if (fd0[0] != STDIN_FILENO) {
                close(fd0[0]);
            }
            if (fd1[1] != STDOUT_FILENO) {
                close(fd0[1]);
            }
            fd0[0] = fd1[0];
            fd0[1] = fd1[1];
            fd1[0] = fd1[1] = -1;
        }
        if (myList->next) {
            fprintf(stderr, "ERROR: MAX_PIPE_LEN (%d) is too small\n",
                    MAX_PIPE_LEN);
        }
    
        if (fd0[0] >= 0 && fd0[0] != STDIN_FILENO) {
            close(fd0[0]);
        }
        if (fd0[1] >= 0 && fd0[1] != STDOUT_FILENO) {
            close(fd0[1]);
        }
        if (fd1[0] >= 0) {
            close(fd1[0]);
        }
        if (fd1[1] >= 0 && fd1[1] != STDOUT_FILENO) {
            close(fd1[1]);
        }
        /* Now wait for the commands to finish */
        int i;
        for (i = 0; i < pipe_len; i++) {
            if (waitpid(pids[pipe_len - 1], &status, 0) == -1) {
                perror("error: waitpid failed");
                failed = 1;
            }
        }
        if (failed)
            status = -1;
        myshell_exit(status);
    }