【问题标题】:Redirect stdout of one process to two processes将一个进程的stdout重定向到两个进程
【发布时间】:2014-01-05 20:30:39
【问题描述】:

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

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

现在,我想使用管道,问题是,我不知道我必须使用 1 个管道还是 2 个管道。

广播.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);
    }

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

现在我只想在我这样做的时候这样做: ./广播 prog1 prog2 用户输入:你好

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

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

【问题讨论】:

  • 哼哼有趣。不知道那件事。但是可以通过简单的分叉和复制来完成吗?
  • 据我所知,并非没有一些代码来读取输入 FD 并将数据写入每个输出 FD。

标签: c fork pipe


【解决方案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 来处理所有系统调用!

【讨论】:

  • 谢谢,但我真的必须用 c 代码 xD 来做。 +1 在 shell 中做
  • 在其他两个程序中,我应该从 fd 0 读取?编辑:是的,它有点像家庭作业。
  • @user3162721 是的 - 这就是他们收到输入时的内容。
  • 嗯,我尝试了代码。它不起作用。它在我第一次执行程序时显示 stange 输入,但是当我输入一些文本时,它只显示主进程“lido ...”的打印,我在 prog1(和 prog2)上执行此操作:#include int main(int argc, char * argv[]) { char buffer[120];读取(0,缓冲区,120); printf("teste1: %s\n", 缓冲区);返回0; }
  • @user3162721 好吧,这个网站不是关于其他人调试你的代码的。我为您提供了在工作中推进所需的基本想法和所有指导方针。如果遇到麻烦,您稍后可能会针对某些特定问题提出更具体的问题。
【解决方案2】:

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

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

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

tee 从标准输入读取并将其发送到prog1prog2tee 默认将标准输入复制到标准输出,因此&gt;/dev/null 用于抑制它。

还有来自moreutils packagepee命令:

$ pee prog1 prog2

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

【讨论】:

  • pee 很有趣,但它不是标准工具,你必须安装它(并且有可能安装它)。源代码很有趣,但它不是最好的开始 - 如果@user3162721 的目的是学习一些东西?那么也许最好尝试修复上面的代码并向他解释问题所在 - 这就是我尝试过的。
【解决方案3】:

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

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

展开:

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

【讨论】:

  • 不明白你的答案:s
  • 我又加了一点,看看有没有帮助
猜你喜欢
  • 2010-11-18
  • 2012-07-14
  • 1970-01-01
  • 1970-01-01
  • 2015-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多