2011-09-03 100 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程序中讀取管道後才能得到最終輸出。