将一个进程的stdout重定向到两个进程

时间:2014-01-05 20:30:40

标签: c fork pipe

我在做我在标题中所说的话时遇到了很大的麻烦。 基本上,我想要一个程序,比如broadcast.c,它接受来自用户的输入,然后将该输入发送到两个进程的输入。 所以,如果要运行此命令: ./broadcast prog1 prog2

它会阻止等待用户的输入,然后将该输入发送到prog1和prog2。

现在,我想使用管道,事情是,我不知道我是否必须使用1个管道或2个管道。

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);
    }

我知道这不起作用,可能会搞砸,所以如果你们能帮助我,那就太棒了。

现在我只想要那样做: ./broadcast prog1 prog2 用户输入:Hello

输出是: prog1说:你好! prog2说:你好!

基本上,prog1和prog2只是在fd 0上使用read打印。

3 个答案:

答案 0 :(得分:1)

可以在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

如果你坚持使用C代码......我在你的代码中发现了很多问题:

  • 重复管道的另一端(0而不是1)
  • 关闭子进程中的其他管道
  • 你应该处理read的返回值,读取的实际字节数 - 并将其传递给write函数
  • 您必须在父母
  • 中关闭孩子的管道末端
  • 父母应该在之后关闭其管道
  • 程序结束时
  • 不必要的dup2来电

从错误的数量我看到你不明白(对不起......)。但基本上我必须赞扬你 - 你创建了2个管道和while循环,这个程序的核心几乎是正确的。我建议你逐步学习小例子:

<强> Linux Documentation Project - pipes in C

这个宝贵的资源将教你如何做管道,如何重定向等。

以下是我尝试修复您的代码:

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);
}

请注意,您应该通过-1检查perror返回值和ERRNO来处理所有系统调用!

答案 1 :(得分:0)

问题是流只能到达一个目的地。您需要打开两个流并向每个目的地发送一个流,并且没有简单的方法我知道这样做。

最简单的方法是让prog1只是通过它接收的任何输入并将其发送到它的输出,然后你可以将它添加到链的中间,它对其他进程实际上是不可见的。

扩展:

在prog 1中的

- 每当它在stdin上接收输入时 - 它以及处理它在stdout上输出相同的数据。这意味着您可以将其添加到链broadcast | prog1 | prog2中,数据将传递到prog2以及由prog1处理。

答案 2 :(得分:0)

  

我想要一个程序,比如broadcast.c,它接受来自用户的输入,然后将该输入发送到两个进程的输入。因此,如果要运行此命令:./broadcast prog1 prog2

您可以使用broadcast实现bash命令:

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

tee从标准输入读取并将其发送到prog1prog2tee默认情况下将stdin复制到stdout,因此使用>/dev/null来禁止它。

pee还有moreutils package命令:

$ pee prog1 prog2

它完全符合您的要求。它使用popen()来运行子进程。实现非常简单,这里是完整的源代码(pee.c来自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));
}