Запись в stdin для доморощенных труб - PullRequest
0 голосов
/ 03 сентября 2011

Я пишу небольшую программу для имитации системы трубопроводов Unix (например, "cat file1.txt | grep keyword | wc").

Мне удается собрать выходные данные программ из stdout, используя dup2() и каналы, но я не могу понять, как передать их в следующий процесс.

Сначала я подумал, что это просто, просто что-то вроде:

write(stdin, buffer, buffer_size);

но это не работает для меня. Существует много информации о stdout, но не так много о stdin.

Любая помощь будет потрясающей.

Ответы [ 2 ]

2 голосов
/ 03 сентября 2011

Как я понимаю ваш вопрос, вы имеете в виду, что вы хотите выполнить команду cat file1.txt | grep keyword | wc из вашей программы.

Вы можете сделать это, настроив несколько каналов между программами с помощью pipe(),Затем fork несколько раз для каждой программы, которую вы хотите выполнить.В разных разветвленных процессах установите stdin и stdout на правильные окончания созданных труб.Когда все настроено, вы можете вызвать exec() для выполнения различных программ, таких как cat, grep и wc с правильными параметрами.

Ваша программа может просто прочитать из канала послеwc программа для получения окончательного результата.

1 голос
/ 04 сентября 2011

Ваш код не будет читать или писать;это будет просто слесарное дело (сортировка труб).

Вы создадите две трубы и двух детей.Первый ребенок выполнит команду cat;вторая команда grep, и родитель запускает программу wc.Поскольку для программ нет путей к файлам, в коде будет использоваться execvp().

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>

static void err_exit(const char *fmt, ...);
static void exec_head(char **args, int *p1, int *p2);
static void exec_tail(char **args, int *p1, int *p2);
static void exec_middle(char **args, int *p1, int *p2);
static void exec_cmd(char **args, int *p1, int *p2);

int main(void)
{
    char *cmd1[] = { "cat", "file1.txt", 0 };
    char *cmd2[] = { "grep", "keyword", 0 };
    char *cmd3[] = { "wc", 0 };
    int pipe12[2];
    int pipe23[2];
    pid_t pid1;
    pid_t pid2;

    if (pipe(pipe12) != 0 || pipe(pipe23) != 0)
        err_exit("Failed to create pipes");

    if ((pid1 = fork()) < 0)
        err_exit("Failed to fork (child1)");
    else if (pid1 == 0)
        exec_head(cmd1, pipe12, pipe23);
    else if ((pid2 = fork()) < 0)
        err_exit("Failed to fork (child2):");
    else if (pid2 == 0)
        exec_middle(cmd2, pipe12, pipe23);
    else
        exec_tail(cmd3, pipe12, pipe23);
    /*NOTREACHED*/
    return(-1);
}

/* Execute head process in pipeline */
/* Close both pipes in p2; connect write end of pipe p1 to stdout */
static void exec_head(char **args, int *p1, int *p2)
{
    if (dup2(p1[1], 1) < 0)
        err_exit("Failed to duplicate file descriptor to stdout (%s)", args[0]);
    exec_cmd(args, p1, p2);
    /*NOTREACHED*/
}

/* Execute tail process in pipeline */
/* Close both pipes in p1; connect read end of p2 to standard input */
static void exec_tail(char **args, int *p1, int *p2)
{
    if (dup2(p2[0], 0) < 0)
        err_exit("Failed to duplicate file descriptor to stdin (%s)", args[0]);
    exec_cmd(args, p1, p2);
    /*NOTREACHED*/
}

/* Execute middle command in pipeline */
/* Connect read end of p1 to stdin; connect write end of p2 to stdout */
static void exec_middle(char **args, int *p1, int *p2)
{
    if (dup2(p1[0], 0) < 0)
        err_exit("Failed to duplicate file descriptor to stdin (%s)", args[0]);
    if (dup2(p2[1], 1) < 0)
        err_exit("Failed to duplicate file descriptor to stdout (%s)", args[0]);
    exec_cmd(args, p1, p2);
    /*NOTREACHED*/
}

/* Close all descriptors for pipes p1, p2 and exec command */
static void exec_cmd(char **args, int *p1, int *p2)
{
    close(p1[0]);
    close(p1[1]);
    close(p2[0]);
    close(p2[1]);
    execvp(args[0], args);
    err_exit("Failed to execute %s", args[0]);
    /*NOTREACHED*/
}

static void err_exit(const char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, ":%d: %s", errnum, strerror(errnum));
    putc('\n', stderr);
    exit(EXIT_FAILURE);
}

Если немного больше поработать над рефакторингом и аккуратно обработать каналы, вы можете исключить exec_head(), exec_tail() и exec_middle() функции (укажите дескриптор, чтобы он стал стандартным, или -1, если он должен оставить stdin один; укажите дескриптор, чтобы стать стандартным, или -1, если он должен оставить стандартный stdout), и вы можете упростить обработку канала, используяодин массив из 4 и соответственно изменяет вызовы к системному вызову pipe().Это затем становится обобщаемым без особых затруднений ... Код, вероятно, должен использовать STDIN_FILENO и STDOUT_FILENO вместо 0 и 1 для второго аргумента dup2().Не определены константы для конца канала для чтения и записи (enum { PIPE_READ, PIPE_WRITE }; здесь может быть полезно).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...