hiaslosch17 hiaslosch17 - 15 days ago 8
C Question

Why does the child process executing uniq -d not print duplicates passed by pipe over stdin?

I am currently writing a program which creates a child process using fork(). This child process shall execute the shell command "uniq" with the option "-d" so it reads from stdin. After the execute command I want to send strings as stdin over a pipe for the "uniq" program. So after I send the same string behind each other the string should be printed on stdout (see

uniq
man page: https://linux.die.net/man/1/uniq). But nothing is printed.

Here is my code so far:

void execute_uniq_process()
{
//create pipe
int pipefd[2];
if(pipe(pipefd)<0)
{
//error handling
}

//create argv for command:
char *argv[] = {"uniq","-d",(char *)0};

//create child process
pid_t pid = fork();
int status;

switch(pid)
{
case -1:
//error handling
break;

case 0:
//child process
//close writing end in pipe
close(pipefd[1]);

//duplicate stdin
dup2(pipefd[0],STDIN_FILENO);
close(pipefd[0]);

execvp("uniq",argv);
exit(errno);
break;

default:
//parent process
//close reading end in pipe
close(pipefd[0]);

//write all commands to pipe
write(pipefd[1],"test1",5);
write(pipefd[1],"test1",5);
write(pipefd[1],"test2",5);
//edited:
close(pipefd[1]);

//waits till child is finished
wait(&status);

if(WEXITSTATUS(status) != EXIT_SUCCESS)
{
//error handling
}
break;
}
}


So I would expect that there is printed "test1" in the shell. I also wonder how to terminate the
uniq
process cleanly. I think there could probably be a issue with write, so that I have to simulate an "enter" after every string I write in the pipe.

Answer

You have to close the pipe before waiting for uniq to finish, because it won't know it's got EOF until you close the pipe. And you've not written even one line to the standard input of uniq because there isn't a newline in the data that was written. The write() system call writes only what you tell it to write; it certainly doesn't add any newlines of its own volition.

Those changes, and sundry trivia, lead to:

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

int main(void)
{
    int pipefd[2];
    if (pipe(pipefd) < 0)
    {
        fprintf(stderr, "Failed to create pipe\n");
        return 1;
    }

    char *argv[] = {"uniq", "-d", (char *)0};

    pid_t pid = fork();

    switch (pid)
    {
    case -1:
        fprintf(stderr, "Failed to fork\n");
        return 1;

    case 0:
        close(pipefd[1]);
        dup2(pipefd[0], STDIN_FILENO);
        close(pipefd[0]);
        execvp(argv[0], argv);
        fprintf(stderr, "Failed to exec %s\n", argv[0]);
        exit(errno);

    default:
        close(pipefd[0]);
        write(pipefd[1], "test1\n", 6);
        write(pipefd[1], "test1\n", 6);
        write(pipefd[1], "test2\n", 6);
        close(pipefd[1]);
        int status;
        int corpse = wait(&status);

        if (WEXITSTATUS(status) != EXIT_SUCCESS)
        {
            fprintf(stderr, "Child (%s) exited (PID %d, status 0x%.4X)\n", argv[0], corpse, status);
        }
        break;
    }
    return 0;
}

This outputs test1 when it is run.

Comments