C: 无法在不丢失另一个的情况下从 2 个管道文件描述符中读取

C: Cannot read from 2 pipe file descriptors without losing the other one

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

遵守以下代码

#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 正在读取 wc 只是 returns 0。

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

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

然后 wc 被读入 buffer2 工作正常,就好像我 运行 终端中的命令 ls | wc,但是 lsbuffer1 不会打印。

我不能两者都得到,只能得到一个或另一个。

如何同时获得两者?

    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 和您的父进程都可以读取该文件。与管道不同,文件允许多次读取相同的数据。