写入stdin用于自制管道

时间:2011-09-03 14:47:32

标签: c pipe stdin

我正在编写一个小程序来模仿Unix管道系统(例如“cat file1.txt | grep keyword | wc”)。

我可以设法使用dup2()和管道从stdout收集程序的输出,但我无法知道如何将其输入到下一个进程。

起初我认为它很简单,就像:

write(stdin, buffer, buffer_size);

但这不适合我。关于stdout的信息很多,但关于stdin的信息差不多。

任何帮助都会很棒。

2 个答案:

答案 0 :(得分:2)

据我了解你的问题,你的意思是你想从你的程序中执行命令cat file1.txt | grep keyword | wc

您可以通过在pipe()的程序之间设置一些管道来实现此目的。 然后fork几次为您要执行的每个程序。 在不同的分叉进程中,将stdinstdout设置为创建的管道的正确结尾。 设置完所有内容后,您可以拨打exec()来使用正确的参数执行catgrepwc等不同的程序。

您的程序可以在wc程序之后从管道中读取以获得最终输出。

答案 1 :(得分:1)

您的代码不会进行任何阅读或写作;它只是管道(整理管道)。

您将创建两个管道和两个子项。第一个孩子将运行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;指定描述符成为stdout,如果它应该单独保留stdout,则为-1),并且可以通过使用单个数组4并修改对pipe()系统调用的调用来简化管道处理因此。然后可以毫不费力地推广......代码应该使用STDIN_FILENOSTDOUT_FILENO而不是0和1作为dup2()的第二个参数。管道的读取和写入端没有定义的常量(enum { PIPE_READ, PIPE_WRITE };在这里可能很有用。)