Search code examples
cpipestdio

Pipes connected to stdio print output after new terminal prompt


I'm having trouble understanding how to do basic piping in C. I looked at a couple other questions on this topic, and either they were for subtly different issues, or I'm so far off the mark on this subject I couldn't understand why the answers are good for my problem.

This program below is just a simple test I made, where I'm trying to get behaviour equivalent to typing "ls | grep a" into my shell. (I have a homework assignment where I have to build a shell that can handle piping, but this is my first step towards understanding pipes to even attempt the homework). I get the correct output, but the terminal prompt ends up appearing before the output, making it look like it did not properly terminate. Since this is connected to a shell homework, I'm worried that will impact the grade (and it just feels wrong to let it look like that anyway). Any advice?

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>

int main()
{
    int fdpipe[2];


    pipe(fdpipe);
    int f1 = fork();
    if(f1 == 0)
    {
        close(fdpipe[1]);
        dup2(fdpipe[0],0);
        close(fdpipe[0]);
        execlp("/bin/grep","grep","a",NULL);
    }
    else
    {
        close(fdpipe[0]);
        dup2(fdpipe[1],1);
        close(fdpipe[1]);
        execlp("/bin/ls","ls",NULL);
        wait(NULL);
    }
    return 0;
}

Here's an example of my terminal output.

1067: ls
a.out  test.c  test.cc
NathanE: ~/Desktop/playground
1068: ./a.out 
NathanE: ~/Desktop/playground
1069: a.out
(The beginning of this line is where my cursor is)

What I'm expecting would be:

1067: ls
a.out  test.c  test.cc
NathanE: ~/Desktop/playground
1068: ./a.out 
a.out
NathanE: ~/Desktop/playground
1069: (my cursor would go here)

Solution

  • The child process runs grep, while the parent replaces itself with ls. The wait(NULL) does nothing, because successful exec*() never return.

    Because the control returns to the shell immediately after ls completes, the shell can display the next prompt before grep completes.

    There are two approaches you can use to avoid this:

    1. fork() both child processes, and wait() for them

    2. Replace the process itself with the last process in the pipe chain

    Either will ensure that control is returned to the shell only after the last process in the pipe chain completes.