Classic C. Using pipes in execvp function, stdin and stdout redirection

krzakov picture krzakov · Dec 10, 2012 · Viewed 37.2k times · Source

I want to simulate bash in my Linux C program using pipes and execvp function. e.g

ls -l | wc -l  

There is my program:

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

This code actually runs, but doesn't do the right thing. What's wrong with this code? That's not working and I don't have a clue why.

Answer

Nicholas Wilson picture Nicholas Wilson · Dec 10, 2012

You need to close the pipe fds in the parent, or the child won't receive EOF, because the pipe's still open for writing in the parent. This would cause the second wait() to hang. Works for me:

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