C: Невозможно прочитать из двух дескрипторов файла канала, не потеряв другой - PullRequest
0 голосов
/ 22 мая 2019

У меня есть код 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, который читает wc, просто возвращает 0.

Если бы я изменил порядок приведенных выше операторов кода, чтобы у меня было следующее:

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

Тогда считывание wc в buffer2 работает нормально, как будто я запускаю команду ls | wc в терминале, однако ls в buffer1 не будет печататься.

Я не могу получить оба, только один или другой.

Как мне получить оба?

1 Ответ

0 голосов
/ 22 мая 2019
    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");
    }

Обратите внимание, что wc читает из link[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, и ваш родительский процесс могут читать этот файл.Файлы позволяют считывать одни и те же данные более одного раза, в отличие от каналов.

...