2011-09-03 77 views
0

我正在编写一个模拟Unix管道系统的小程序(例如“cat file1.txt | grep keyword | wc”)。向stdin写入自制管道系统

我可以设法使用dup2()和管道从stdout收集程序的输出,但是我无法弄清楚如何将它送到下一个进程。

起初我还以为是简单的,只是这样的:

write(stdin, buffer, buffer_size); 

但是这不是为我工作。关于标准输出有很多信息,但没有太多关于标准输入的信息。

任何帮助将是惊人的。

+0

你想从标准输入中读取'而不是'写入'它。 – GWW

+0

@GWW吗?但我不想*写入*? 我可能只是努力想象这一点,因为如果我*从输出读*只是假设你*写*到输入... – TrewTzu

+0

在过程之一(即猫)你想写入标准输出,然后在过程两个(即。grep)你想从​​标准输入读取,然后写入标准输出,最后在进程3(wc)你想从标准输入读取并写入标准输出 – GWW

回答

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,如果它要标准输入独自离开;指定描述符变成标准输出,如果它应该只保留标准输出,则为-1),并且可以通过使用单个数组4来简化管道处理,并相应地将调用修改为pipe()系统调用。然后就变得很普遍了......代码可能应该使用STDIN_FILENOSTDOUT_FILENO而不是0和1作为dup2()的第二个参数。管道的读写端没有定义的常量(这里可能有用enum { PIPE_READ, PIPE_WRITE };)。

2

正如我理解你的问题,你的意思是你想要在你的程序中执行命令cat file1.txt | grep keyword | wc

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

你的程序只能从wc程序中读取管道后才能得到最终输出。