Classic C.在execvp函数,stdin和stdout重定向中使用管道

krz*_*kov 8 c redirect fork execvp

我想使用管道和execvp函数在我的Linux C程序中模拟bash.例如

ls -l | wc -l  
Run Code Online (Sandbox Code Playgroud)

有我的计划:

if(pipe(des_p) == -1) {perror("Failed to create pipe");}

if(fork() == 0) {    //first fork
  close(1);          //closing stdout
  dup(des_p[1]);     //replacing stdout with pipe write 
  close(des_p[0]);   //closing pipe read
  close(des_p[1]);   //closing pipe write

  if(execvp(bash_args[0], bash_args)) // contains ls -l
    /* error checking */
}
else {
  if(fork() == 0) {  //creating 2nd child
    close(0);        //closing stdin
    dup(des_p[0]);   //replacing stdin with pipe read
    close(des_p[1]); //closing pipe write
    close(des_p[0]); //closing pipe read

    if(execvp(bash_args[another_place], bash_args)) //contains wc -l
      /* error checking */
  }

  close(des_p[0]);
  close(des_p[1]);
  wait(0);
  wait(0);
}
Run Code Online (Sandbox Code Playgroud)

这段代码实际上运行,但没有做正确的事情.这段代码出了什么问题?这不起作用,我不知道为什么.

Nic*_*son 32

您需要关闭父级中的管道fds,否则子级将不会收到EOF,因为管道仍然打开以便在父级中写入.这会导致第二个wait()挂起.适合我:

#include <unistd.h>
#include <stdlib.h>


int main(int argc, char** argv)
{
        int des_p[2];
        if(pipe(des_p) == -1) {
          perror("Pipe failed");
          exit(1);
        }

        if(fork() == 0)            //first fork
        {
            close(STDOUT_FILENO);  //closing stdout
            dup(des_p[1]);         //replacing stdout with pipe write 
            close(des_p[0]);       //closing pipe read
            close(des_p[1]);

            const char* prog1[] = { "ls", "-l", 0};
            execvp(prog1[0], prog1);
            perror("execvp of ls failed");
            exit(1);
        }

        if(fork() == 0)            //creating 2nd child
        {
            close(STDIN_FILENO);   //closing stdin
            dup(des_p[0]);         //replacing stdin with pipe read
            close(des_p[1]);       //closing pipe write
            close(des_p[0]);

            const char* prog2[] = { "wc", "-l", 0};
            execvp(prog2[0], prog2);
            perror("execvp of wc failed");
            exit(1);
        }

        close(des_p[0]);
        close(des_p[1]);
        wait(0);
        wait(0);
        return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 有一个非常可靠的经验法则:_如果你使用`dup()`或`dup2()`将管道的一端复制到标准输入或标准输出,你需要`close()`原始管道的两端._在某些情况下,这可能不是必要的行为,但很少遇到这种情况. (5认同)
  • @Khan您真的应该发布一个新问题,因为您实际上是在寻求帮助来调试代码!您确实需要关闭孩子使用的每个fd,您的代码有一个不相关的问题。在注释行中,您正在关闭循环的下一个迭代将要使用的fd!您应该在父级中关闭所有fds-直到在父级中使用完它们之后,才可以关闭它们。如果您检查了dup2的返回码,就会注意到(您还需要检查pipe / fork / close / execvp / waitpid的返回码)。 (2认同)