【问题标题】:Piping to stdout管道到标准输出
【发布时间】:2018-06-03 18:01:22
【问题描述】:

编辑:不要尝试将管道末端连接到标准输出。将管道的输入连接到标准输出,将管道的输出连接到标准输入。

我想使用 pipe() 和 dup2() 将子进程的标准输出通过管道传输到其母进程的标准输出。 在我的示例中,我尝试在孩子中打印一个字符串,该字符串将其标准输出重定向到管道;然后让字符串出现在母亲的标准输出中。但是,输出永远不会出现在母进程的标准输出中。怎么回事?

代码

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

int main(int argc, char *argv[]) 
{   
        // This program should print 1, 2 and 4; but only prints 1 and 4.

        int stdout_cpy = dup(1);

        printf("1. stdout working\n");

        int pipe1[2];
        pipe(pipe1);

        int pid = fork();

        if (pid == 0) {
                // child
                dup2(pipe1[1], 1); // stdout out to pipe in
                close(pipe1[0]);
                fprintf(stdout, "2. This should print in the mother's stdout\n");
                exit(0);
        } else {
                // mother
                close(pipe1[1]);
                dup2(pipe1[0], 1); // stdout from pipe out
        }

        /* 2. should print in parent's stdout... */
        int status;
        while (wait(&status) > 0);

        printf("3. This should not print\n");

        dup2(stdout_cpy, 1);
        close(pipe1[0]);

        printf("4. stdout redirected, done\n");

        return 0;
}

输出

1. stdout working
4. stdout redirected, done

【问题讨论】:

  • 你为什么要给pipe打两次电话?你的意思是第二次拨打fork
  • 在父进程中,dup2(pipe1[0], 1)是错误的。 pipe1[0] 是管道的 read 端,为什么要将它复制到 write 描述符?你所做的通常是不正确的。子进程写入标准输出,而在父进程中,您需要从管道的读取端读取该输出。您不能真正从子进程直接写入父进程标准输出描述符。反正不是这样的。
  • 使用不确定的对象值的未定义行为。
  • 正确的做法是不使用自动存储时长不确定的对象的值。
  • 而且由于您不初始化 stdout_cpy,代码 int stdout_cpy; dup2(1, stdout_cpy); 可能很容易失败 — 但您不检查。在上下文中,您可以使用int stdout_cpy = dup(1);

标签: c pipe stdout


【解决方案1】:

这是您的代码的工作版本。为了显示子级的输出,父级必须从管道中读取它,然后将其写入自己的标准输出,这就是这段代码的作用。如果管道没有读取任何内容,则写入管道的输出将不会出现在任何地方。这种变化使得重复的标准输出基本无关紧要。

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

int main(void)
{   
    int stdout_cpy = dup(1);            // Mostly irrelevant

    printf("1. stdout working\n");

    int pipe1[2];
    pipe(pipe1);

    int pid = fork();

    if (pid == 0)
    {
        // child at work
        dup2(pipe1[1], 1);              // stdout out to write end of pipe
        close(pipe1[0]);                // Close both ends of the pipe!
        close(pipe1[1]);                // Close both ends of the pipe!
        printf("2. This should be read by parent from stdin and be printed to the parent's stdout\n");
        exit(0);
    }

    /* Parent at work */
    dup2(pipe1[0], 0);                  // stdin from from read end of pipe
    close(pipe1[0]);                    // Close both ends of the pipe!
    close(pipe1[1]);                    // Close both ends of the pipe!

    // Read data written on pipe by child, and write to stdout
    char buffer[512];
    int nbytes = read(0, buffer, sizeof(buffer));
    if (nbytes > 0)
        write(1, buffer, nbytes);

    /* 2. should print in parent's stdout... */
    int status;
    int corpse;
    while ((corpse = wait(&status)) > 0)
        printf("%d: child %d exited with status 0x%.4X\n", (int)getpid(), corpse, status);

    printf("3. This should print too\n");

    dup2(stdout_cpy, 1);                // Mostly irrelevant
    //close(pipe1[0]);                  // Long since closed
    close(stdout_cpy);                  // No longer needed (closed on exit anyway)

    printf("4. stdout redirected, done\n");

    return 0;
}

样本输出:

1. stdout working
2. This should be read by parent from stdin and be printed to the parent's stdout
8008: child 8009 exited with status 0x0000
3. This should print too
4. stdout redirected, done

或者,如果您只是希望子输出与父输出显示在同一设备上,则根本不要使用管道;孩子将继承父母的标准输出,无需任何额外帮助即可对其进行写入。

【讨论】:

  • 感谢您的解释。我看到我的错误是假设我可以将管道的输出直接连接到标准输出。根据您的继承建议,我对我的工作进行了一些调整,现在它可以完全按照我的意愿工作。感谢您的帮助。