C:无法从2个管道文件描述符中读取而不会丢失另一个

时间:2019-05-22 05:03:07

标签: c unix pipe file-descriptor

我有C代码正在模拟bash命令ls | wc。我要实现的目标之一是能够读取每个命令的输出,以便可以打印它们-当ls被管道传输时,它们既可以打印wc也可以打印ls。我面临的这个问题是,每当我阅读其中一个命令时,我都会以某种方式失去另一个命令。

观察以下代码

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

#define LS_PATH "/bin/ls"
#define WC_PATH "/usr/bin/wc"

int main()
{
    pid_t pid;

    int link[2], link2[2];

    char *const arg1[] = {"ls", NULL};
    char *const arg2[] = {"wc", NULL};

    char *buffer1[4096], buffer2[4096];

    pipe(link);
    pipe(link2);

    pid = fork();

    if (pid == 0)
    {
        dup2(link[1], STDOUT_FILENO);
        close(link[0]);
        close(link[1]);
        execv(LS_PATH, arg1);
        perror("error1");
    }
    else
    {

        pid = fork();

        if (pid == 0)
        {
            dup2(link[0], STDIN_FILENO);
            dup2(link2[1], STDOUT_FILENO);
            close(link[1]);
            close(link[0]);
            close(link2[1]);
            close(link2[0]);
            execv(WC_PATH, arg2);
            perror("error2");
        }
        else
        {

            close(link[1]);

            close(link2[1]);

            // the following two lines of code is the point of interest
            read(link[0], buffer1, sizeof(buffer1));   // ls
            read(link2[0], buffer2, sizeof(buffer2));  // wc

            printf("%s\n", buffer1);

            printf("%s\n", buffer2);
        }
    }
}

主要关注以下代码语句:

read(link[0], buffer1, sizeof(buffer1));   // ls
read(link2[0], buffer2, sizeof(buffer2));  // wc

ls首先被读取到buffer1中,它可以正常打印,但是随后读取buffer2的{​​{1}}仅返回0。

如果我要切换上述代码语句的顺序,以便获得以下内容:

wc

然后将read(link2[0], buffer2, sizeof(buffer2)); // wc read(link[0], buffer1, sizeof(buffer1)); // ls 读入wc就像在终端中运行命令buffer2一样工作正常,但是ls | wc中的ls无法打印

我不能全都得到,只有一个。

如何同时获得两者?

1 个答案:

答案 0 :(得分:0)

    if (pid == 0)
    {
        dup2(link[0], STDIN_FILENO); // ** HERE **
        dup2(link2[1], STDOUT_FILENO);
        close(link[1]);
        close(link[0]);
        close(link2[1]);
        close(link2[0]);
        execv(WC_PATH, arg2);
        perror("error2");
    }

请注意,wclink[0]中读取。

        // the following two lines of code is the point of interest
        read(link[0], buffer1, sizeof(buffer1));   // ls   ** HERE **
        read(link2[0], buffer2, sizeof(buffer2));  // wc

请注意,父进程也尝试读取link[0]

您不能同时获得两者,因为写入管道的数据只能从其中读取一次。如果wc读取ls的输出以计算其字符,那么您也无法在父级中读取ls的输出。如果父级从管道中读取ls的输出,那么wc也将无法获得它,因为它不再在管道中。这就是为什么您只能获得其中一个的原因-如果一件事读到管道,那么另一件事就不在管道中。

有多种方法可以解决此问题,具体取决于您的要求。

  1. 您可以使用三个管道,其中多余的管道是wc的标准输入。父进程必须将数据从具有ls输出的管道复制到作为wc输入的管道。

  2. 您可以添加第三个进程,即复制数据的tee实例。

  3. 您可以ls写入临时文件。 wc和您的父进程都可以读取该文件。与管道不同,文件允许多次读取相同的数据。