Реализация трубы в C

Я пытаюсь реализовать канал в C. eg - $ ls | wc | wc

Я написал следующий код -

#include<stdio.h>
#include<stdlib.h>
#include <unistd.h>

void run_cmd(char *cmd, int* fd_in, int* fd_out)
{
    int c = fork();

    if (c==0)
    {
        if (fd_in != NULL)
        {
            close(fd_in[1]);
            dup2(fd_in[0], 0);
        }
        if (fd_out != NULL)
        {
            close(fd_out[0]);
            dup2(fd_out[1],1);
        }
        execlp(cmd, cmd, NULL);
    }
}

int main(int argc, char **argv)
{
    int fd_1[2], fd_2[2], i;
    pipe(fd_1);
    pipe(fd_2);

    run_cmd(argv[1], NULL, fd_1);

    for( i=2; i<argc-1; i++)
    {
        if (i%2 == 0)
            run_cmd(argv[i], fd_1, fd_2);
        else
            run_cmd(argv[i], fd_2, fd_1);
    }
    if (i%2 == 0)
        run_cmd(argv[i], fd_1, NULL);
    else
        run_cmd(argv[i], fd_2, NULL);
}

Это отлично работает с двумя аргументами, например - $./a.out ls wc

Но когда я пытаюсь использовать более двух аргументов, это не работает.

Кто-нибудь может сказать, что случилось с моим кодом, или любым другим способом сделать это. Любая помощь будет высоко оценена.

Ответ 1

Это практически не проверяет ошибки, но почему так сложно?

int main (int argc, char ** argv) {
    int i;

    for( i=1; i<argc-1; i++)
    {
        int pd[2];
        pipe(pd);

        if (!fork()) {
            dup2(pd[1], 1); // remap output back to parent
            execlp(argv[i], argv[i], NULL);
            perror("exec");
            abort();
        }

        // remap output from previous child to input
        dup2(pd[0], 0);
        close(pd[1]);
    }

    execlp(argv[i], argv[i], NULL);
    perror("exec");
    abort();
}

Ответ 2

Если вас все еще интересует, почему ваш источник не работал (решение Сергея в любом случае лучше):

Проблема заключается не в закрытии стороны записи fd_1 в родительском процессе. Таким образом, как argv[1], так и родительские были писателями этого канала, что вызвало путаницу. Пожалуйста, не запрашивайте более подробную информацию (особенно, почему проблема не возникает, если вы используете только один канал), но исходный исходный код будет работать с древовидными процессами, если вы просто добавите close( fd_1[1] ); после первого вызова run_cmd()