cpipestdoutgetsunnamed-pipes

What is the importance of adding "\n" to stdout when it's redirected to another process?


So, I'm playing with pipes in c, and I have an exercise where I call a program through command line as this: "./self 1" which then calls itself with execlp but with an argument 2: "./self 2" which further on calls itself with argument 3: "./self 3". The point of these processes is this: process1 takes a line from keyboard and puts it in pipe1, then process2 gets the line from pipe1 and puts it in pipe2, then process3 gets it from pipe2 and counts the number of space characters. This code never works if I dont print a newline character on the screen before taking inputs with fprintf(stdout,"\n"); . Why is that?

Here is the code:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    
    if (strcmp(argv[1], "1") == 0) {
        int fdpipe1[2];
        if (pipe(fdpipe1)) {
            printf("Error pipe1\n");
            return 0;
        }
        pid_t p;
        p = fork();
        
        if (p == 0) {
            close(fdpipe1[1]);
            dup2(fdpipe1[0], 0);
            execlp("./self", "./self", "2", NULL);
        } else {
            close(fdpipe1[0]);
            fprintf(stdout, "\n");
            dup2(fdpipe1[1], 1);
            char input[100];
            gets(input);
            puts(input);
            wait(NULL);
        }
    }
    else if (strcmp(argv[1], "2") == 0) {
        int fdpipe2[2];
        if (pipe(fdpipe2)) {
            printf("Error pipe2\n");
            return 0;
        }
        pid_t p;
        p = fork();
        if (p == 0) {
            close(fdpipe2[1]);
            dup2(fdpipe2[0], 0);
            execlp("./self", "./self", "3", NULL);
        } else {
            close(fdpipe2[0]);
            fprintf(stdout, "\n");
            dup2(fdpipe2[1], 1);
            char input[100];
            gets(input);
            puts(input);
            wait(NULL);
        }
    } 
    else if (strcmp(argv[1], "3") == 0) {
        char input[100];
        gets(input);
        int i = 0, counter = 0;
        while (input[i] != '\0') {
            if (input[i++] == ' ') counter++;
        }
        printf("%d\n", counter);
    }
    return;
}

Solution


  • In this kind of construct, when you connect stdout from a process to stdin of another process via unnamed pipe, a newline character is added usually to ensure the stream is sent, i.e. the stdout buffer is flushed, as a parallel example, when you use scanf, only when you hit enter (a newline is added to stdin) is the stream read, a similar principle applies here.

    I would suggest you use STDIN_FILENO and STDOUT_FILENO built in macros instead of the hard coded file descriptors, if not for anything else, it makes the code more readable for someone who is unfamiliar with the matter.


    Please avoid using gets, this is a dangerous function, it does not check the bounds of the destination buffer, it can cause all kinds of trouble, so much so it was deprecated and later removed from the standard, though it still can be used with some compilers, for legacy reasons I would imagine, check this fantastic answer on a post about this topic:

    Why is the gets function so dangerous that it should not be used?

    The advice is to use fgets instead.