Redirect stdout of one process to two processes

♀尐吖头ヾ 提交于 2020-01-04 04:55:58

问题


Im having big troubles in doing what i said in title. Basically, i want a program, say broadcast.c, that accepts input from the user and then sends that input to the input of two processes. So if would run this command: ./broadcast prog1 prog2

It would block awaiting the input from the user and then sending that input to prog1 and prog2.

Now, i want to use pipes, thing is, i dont know if i have to use 1 pipe or 2 pipes.

broadcast.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

    int main(int argc, char* argv[]) {
        int fds1[2], fds2[2];
        char buffer[120];
        pipe(fds1);
        pipe(fds2);

        if (fork() == 0) {
            close(0);
            dup(fds1[0]);
            close(fds1[0]);
            close(fds1[1]);
            execl(argv[1], argv[1], NULL);
        }

        if (fork() == 0) {
            close(0);
            dup(fds2[0]);
            close(fds2[0]);
            close(fds2[1]);
            execl(argv[2], argv[2], NULL);
        }

        while(read(0, buffer, 120) != 0) {
            printf("lido: %s\n", buffer);
            write(fds1[0],buffer,120);
            write(fds2[0],buffer,120);
        }

        close(1);
        dup(fds1[1]);
        dup(fds2[1]);
        exit(0);
    }

I know this doesnt work and it may be messed up, so if you guys could help me out that would be great.

For now i just want that when i do: ./broadcast prog1 prog2 The user enters: Hello

The output is: prog1 says: Hello! prog2 says: Hello!

Basically prog1 and prog2 are just print using read on fd 0.


回答1:


i want a program, say broadcast.c, that accepts input from the user and then sends that input to the input of two processes. So if would run this command: ./broadcast prog1 prog2

You could implement broadcast command using bash:

$ tee >/dev/null >(prog1) >(prog2)

tee reads from stdin and sends it to prog1 and prog2. tee by default duplicates stdin to stdout therefore >/dev/null is used to suppress it.

There is also pee command from moreutils package:

$ pee prog1 prog2

It does exactly what you want. It uses popen() to run child processes. The implementation is very simple, here's the whole source code (pee.c from git://git.kitenet.net/moreutils):

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>

/* Licensed under the GPL
 * Copyright (c) Miek Gieben, 2006
 */

/* like tee(1), but then connect to other programs using
 * pipes _and_ output to standard output
 */

int
close_pipes(FILE **p, size_t i) 
{
    int ret=EXIT_SUCCESS;
    size_t j;
    for (j = 0; j < i; j++) {
        int r = pclose(p[j]);
        if (WIFEXITED(r))
            ret |= WEXITSTATUS(r);
        else
            ret |= 1;
    }
    return ret;
}

int
main(int argc, char **argv) {
    size_t i, r;
    FILE **pipes;
    char buf[BUFSIZ];

    pipes = malloc(((argc - 1) * sizeof *pipes));
    if (!pipes) 
        exit(EXIT_FAILURE);

    for (i = 1; i < argc; i++) {
        pipes[i - 1] = popen(argv[i], "w");
        if (!pipes[i - 1]) {
            fprintf(stderr, "Can not open pipe to '%s\'\n", argv[i]);
            close_pipes(pipes, argc);

            exit(EXIT_FAILURE);
        }
    }
    argc--;

    while(!feof(stdin) && (!ferror(stdin))) {
        r = fread(buf, sizeof(char), BUFSIZ, stdin);
        for(i = 0; i < argc; i++) {
            if (fwrite(buf, sizeof(char), r, pipes[i]) != r) {
                fprintf(stderr, "Write error to `%s\'\n", argv[i + 1]);
                close_pipes(pipes, argc);
                exit(EXIT_FAILURE);
            }
        }
    }
    exit(close_pipes(pipes, argc));
}



回答2:


It can be easily done in shell:

FIFO_FILE=/tmp/fifo$$
mkfifo $FIFO_FILE
cat $FIFO_FILE | prog1 &
cat | tee $FIFO_FILE | prog2
wait # wait for everything to finish
rm -f $FIFO_FILE

If you insist on the C code... There are so many problems I have found in your code:

  • duping the other end of pipe (0 instead of 1)
  • close the other pipe in the child processes
  • you should handle return value of read, the actual number of bytes read - and pass it to the write function
  • you must close the child's ends of pipes in the parent
  • parent should close its pipes afterwards
  • unnecesasry dup2 calls at the end of the program

From the number of mistakes I see you don't understand it (sorry...). But basicly I must commend you - you created the 2 pipes and the while loop, this core of the program was almost correct. I recommend you to start learning step by step on small examples:

Linux Documentation Project - pipes in C

This valuable resource will teach you how to do pipes, how to redirect etc.

Here is my attempt to fix your code:

int main(int argc, char* argv[]) {
    int fds1[2], fds2[2];
    char buffer[120];
    int size;

    pipe(fds1);
    pipe(fds2);

    if (fork() == 0) {
        close(0);
        dup(fds1[1]);
        close(fds1[0]);
        close(fds1[1]);
        close(fds2[0]);
        close(fds2[1]);
        execl(argv[1], argv[1], NULL);
    }

    if (fork() == 0) {
        close(0);
        dup(fds2[1]);
        close(fds1[0]);
        close(fds1[1]);
        close(fds2[0]);
        close(fds2[1]);
        execl(argv[2], argv[2], NULL);
    }

    close(fds1[1]);
    close(fds2[1]);

    while((size = read(0, buffer, 120)) != 0) {
        printf("lido: %s\n", buffer);
        write(fds1[0],buffer,size);
        write(fds2[0],buffer,size);
    }

    close(fds1[0]);
    close(fds1[0]);

    exit(0);
}

Note that you should handle all syscalls by checking for -1 return value and the ERRNO by perror!




回答3:


The problem is that streams can only go to one destination. You would need to open two streams and send one to each destination and there is no easy way I'm aware of to do that.

What may be simplest is to make prog1 just pass through any input it receives and send it on it's output, then you can just add it in the middle of the chain and it is effectively invisible to the other processes.

To expand:

in prog 1 - whenever it receives input on stdin - it as well as processing it outputs the same data on stdout. This means that you can add it into a chain broadcast | prog1 | prog2 and the data will get passed through to prog2 as well as being processed by prog1.



来源:https://stackoverflow.com/questions/20938782/redirect-stdout-of-one-process-to-two-processes

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!