POSIX Pipes Creating a shell-style pipeline


Example

A shell-style pipeline consists of two or more processes, each one with its standard output connected to the standard input of the next. The output-to-input connections are built on pipes. To establish a pipeline-like set of processes, one creates pipe-connected child processes as described in another example, and furthermore uses the dup2(2) function to duplicate each pipe end onto the appropriate standard file descriptor of its process. It is generally a good idea to then close the original pipe-end file descriptor, especially if, as is often the case, one intends for the child to afterward exec a different command.

// Most error handling omitted for brevity, including ensuring that pipe ends are
// always closed and child processes are always collected as needed; see other
// examples for more detail.
int demo() {
    int pipefds[2];
    pid_t child1 = -1, child2 = -1;

    pipe(pipefds);

    switch (child1 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 1 */
            close(pipefds[0]);
            dup2(pipefds[1], STDOUT_FILENO);
            close(pipefds[1]);
            execl("/bin/cat", "cat", "/etc/motd", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    switch (child2 = fork()) {
        case -1:
            // handle error ...
            break;
        case 0:   /* child 2 */
            close(pipefds[1]);
            dup2(pipefds[0], STDIN_FILENO);
            close(pipefds[0]);
            execl("/bin/grep", "grep", "[Ww]ombat", NULL);
            exit(1);  // execl() returns only on error
        default:  /* parent */
            // nothing
    }

    // Only the parent reaches this point
    close(pipefds[0]);
    close(pipefds[1]);
    wait(NULL);
    wait(NULL);
}

One of the more common errors in setting up a pipeline is to forget to have one or more of the processes involved close the pipe ends that it is not using. As a rule of thumb, the end result should be that each pipe end is owned by exactly one process, and is open only in that process.

In a case such as the demo function above, failure the second child or of the parent to close pipefds[1] will result in the second child hanging, for grep will continue to wait for input until it sees EOF, and that will not be observed as long as the write end of the pipe remains open in any process, such as the parent process or the second child itself.