【问题标题】:Piping Given an Array of UNIX Commands给定一组 UNIX 命令的管道
【发布时间】:2012-09-26 23:33:42
【问题描述】:

所以我正在使用 C 创建一个基本的 UNIX shell 项目的最后阶段。我已经完成了许多不同的程序部分,但现在我想征服管道。我特别想创建一个可以处理任意数量管道的程序。

由于某种原因,我的代码到达某行(标记为://DIES HERE)然后停止,我不知道为什么。

这是我目前的代码:

//the contents of args[0] is {"ls","-l","-o"}
//the contents of args[1] is {"wc","-l"}

int pipefd[2];

pipe(&pipefd[0]);   // Error check!

fflush(stdout);
for (i = 0; i < commands; i++){
    int pid = fork();

    if (pid == 0){

        int command_no = i;
        int prev_pipe = ((command_no - 1) % 2) * 2;
        int current_pipe = (command_no % 2) * 2;
        printf("\ncmd %d: prev pipe %d, curr pipe %d\n\n", i, prev_pipe, current_pipe);
        fflush(stdout);

        // If current command is the first command, close the
        // read end, else read from the last command's pipe
        if (command_no == 0){
            close(pipefd[0]);
        }
        else{
            dup2(pipefd[prev_pipe], 0);
            close(pipefd[current_pipe]);                    
        }

        // If current command is the last command, close the
        // write end, else write to the pipe
        if (command_no == commands - 1){
            close(pipefd[current_pipe + 1]);                    
        }
        else{
            dup2(pipefd[current_pipe + 1], 1); //DIES HERE
        }
        // printf("Here?\n\n");
        execvp(*args[i], args[i]);
        fprintf(stderr, "Failed to exec: %s (%d: %s)\n", arrayOfCommands[i], errno, strerror(errno));
        _exit(1);
    }
}

感谢任何帮助! :)

【问题讨论】:

  • 如果 execvp() 失败,您应该退出(非零数)。 0 用于在 Unix 中表示成功。
  • 不要忘记将所有管道进程放在一个进程组中(第一个进程作为进程组负责人)。这允许您等待整个组结束(参见“waitpid”或更好的“waitid”函数)。我建议查看 bash 源代码。不仅如此:在调试模式下编译 bash 并在运行时对其进行调试:它会让您大开眼界。

标签: c shell unix piping


【解决方案1】:

我看到的主要问题是pipe() 在循环之外。您将需要在每对进程之间创建一个新的pipe()。您的问题中的 cmets 也提出了一些要点。

我多年前在大学里写了一个 shell,下面是我代码中的类似循环。我确定我现在会做很多不同的事情,但它可能对你有用:

    for (i = 0; i < iNumPipes; ++i) {
            if (i == iNumPipes - 1) {
                    /* this is the last command
                     */
                    p[1] = fdOutput;
                    p[0] = -1;
            } else if (-1 == pipe(p)) {
                    perror("pipe");
                    exit(1);
            }

            switch (iPid = fork()) {
            case -1:
                    perror("fork");
                    exit(1);
            case 0:
                    close(0);
                    dup2(fdInput, 0);
                    close(fdInput);

                    close(1);
                    dup2(p[1], 1);
                    close(p[1]);

                    if (-1 != fdErr) {
                            close(2);
                            dup2(fdErr, 2);
                            close(fdErr);
                    }

                    pc = SearchPath(pppcAvs[i][0]);
                    execve(pc, pppcAvs[i], ppcEnv);
                    perror(pc);
                    _exit(-1);
            default:
                    close(fdInput);
                    close(p[1]);
                    fdInput = p[0];
            }

    }

【讨论】:

  • 这太棒了!你介意分享一下 fdErr 是什么吗?我很难弄清楚。
  • 其实更好的是,您介意分享一下 fdOutput、fdInput 和 fdErr 是如何定义的吗?
  • 它们都是文件描述符(因此是 fd 前缀,因为我当时使用匈牙利符号)所以它们是 int。 shell 对stderr 有特殊的重定向规则,所以fdErr 是“全局”错误重定向。 fdOutputfdInput 只是将链的跃点绑在一起。在那个循环之前,它们只是设置为dup(0)dup(1),除非命令行上有文件重定向。
  • 再次感谢您的回复。我仍然很难让您的代码正常工作,主要是因为我对 C 语言特别是管道很陌生。你能给我一个关于如何设置 fdOutput、fdInput 和 fdErr 的“傻瓜”解释吗?
  • 只是int fdInput = dup(0)int fdOutput = dup(1)。您也可以设置int fdErr = dup(2)。如果你想要文件,你可以用 open(...) 替换其中的任何一个
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-28
  • 1970-01-01
  • 2015-04-22
  • 2010-09-08
  • 1970-01-01
  • 1970-01-01
  • 2018-02-28
相关资源
最近更新 更多