title: c - Pipe between 3 child processes
body: <p>I can't seem to find a good explanation of how to pipe data through three child processes.  I essentially want to do this:</p>
  
  <p>ls -al / | tr a-j A-J | tr k-z K-Z</p>
  
  <p>I've been able to set up two child processes to emulate the first two parts of the command, and the output is correct.</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  
  main()
  {
      int pi[2];
      pipe(pi);
  
      //  First child
      if ( fork() == 0 )
      {
          close(pi[0]);
          dup2(pi[1], 1);
          close(pi[1]);
          execlp( "ls", "ls", "-al", NULL );
      }
  
      //  Second child
      if ( fork() == 0 )
      {
          close(pi[1]);
          dup2(pi[0], 0);
          close(pi[0]);
          execlp( "tr", "tr", "a-j", "A-J", NULL );
      }
  
      //  Close parent's copy of pipe
      close(pi[0]);
      close(pi[1]);
  
      wait(NULL);
  }
  </code></pre>
  
  <p>The only way that I can think of to pass the output to a third process is to create a second pipe.  I would then use the second pipe to pass the data from the second child to the third child.  Unfortunately, when I try to do this, I get no output at all.</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  
  main()
  {
      //  Create the two pipes
      int pi[2];
      pipe(pi);
      int pi2[2];
      pipe(pi2);
  
      //  First child
      if ( fork() == 0 )
      {
          //  Set up the first pipe to be written to
          close(pi[0]);
          dup2(pi[1], 1);
          close(pi[1]);
          execlp( "ls", "ls", "-al", NULL );
      }
  
      //  Second child
      if ( fork() == 0 )
      {
          //  Set up first pipe to be read from
          close(pi[1]);
          dup2(pi[0], 0);
          close(pi[0]);
          //  Set up second pipe to be written to
          close(pi2[0]);
          dup2(pi2[1], 1);
          close(pi2[1]);
          execlp( "tr", "tr", "a-j", "A-J", NULL );
      }
  
      //  Third child
      if ( fork() == 0 )
      {
          //  Set up second pipe to be read from
          close(pi2[1]);
          dup2(pi2[0], 0);
          close(pi2[0]);
          execlp( "tr", "tr", "k-z", "K-Z", NULL );
      }
  
      //  Close parent's copy of first and second pipes
      close(pi[1]);
      close(pi[0]);
      close(pi2[1]);
      close(pi2[0]);
  
      wait(NULL);
  }
  </code></pre>
  
  <p>I'm not entirely sure where to go from here.  Is there a coding error, or is it the wrong approach?</p>
  
  <p>Edit:  I found the problem.  The endpoints on pipe(pi) were duplicated into the child process and never closed.  The solution is to either close the endpoints in the parent before the third child is forked so that it doesn't get its own copy, or to close its copies of the endpoints.</p>
  
  <p>Below I've closed the endpoints before the third child.  I also didn't create pipe "pi2" until after the first child where it is actually used.  I think in theory I was supposed to be closing the endpoints for pipe "pi2" in the first child, but in practice it worked anyway.  Moving the creation of the pipe to after the first child removes any concern.</p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;unistd.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;string.h&gt;
  
  main()
  {
      //  Create the two pipes
      int pi[2];
      pipe(pi);
      int pi2[2];
  
      //  First child
      if ( fork() == 0 )
      {
          //  Set up the first pipe to be written to
          //  and close the unneeded pipe endpoints
          close(pi[0]);
          dup2(pi[1], 1);
          close(pi[1]);
          execlp( "ls", "ls", "-al", NULL );
      }
  
      //  Don't open the second pipe until it's needed
      //  so extra endpoints don't need to be closed
      pipe(pi2);
  
      //  Second child
      if ( fork() == 0 )
      {
          //  Set up first pipe to be read from
          //  and close the unneeded pipe endpoints
          close(pi[1]);
          dup2(pi[0], 0);
          close(pi[0]);
          //  Set up second pipe to be written to
          //  and close the unneeded pipe endpoints
          close(pi2[0]);
          dup2(pi2[1], 1);
          close(pi2[1]);
          execlp( "tr", "tr", "a-j", "A-J", NULL );
      }
  
      //  Close original pipe endpoints so they aren't
      //  duplicated into the third child (where they would
      //  need to be closed).
      close(pi[1]);
      close(pi[0]);
  
      //  Third child
      if ( fork() == 0 )
      {
          //  Set up second pipe to be read from
          //  and close the unneeded pipe endpoints
          close(pi2[1]);
          dup2(pi2[0], 0);
          close(pi2[0]);
          execlp( "tr", "tr", "k-z", "K-Z", NULL );
      }
  
      //  Close parent's copy of second pipes
      close(pi2[1]);
      close(pi2[0]);
  
      wait(NULL);
  }
  </code></pre>