2014-01-23 40 views
7

我知道這個話題已經出現好幾次了,但我仍然堅持一點。 我需要編寫一個模擬cmd1 | cmd2 | cmd3 ...管道的程序。Unix中的遞歸管道再次

我的代碼是在這裏:http://ideone.com/fedrB8

#include <stdio.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <stdlib.h> 

void pipeline(char * ar[], int pos, int in_fd); 
void error_exit(const char*); 
static int child = 0; /* whether it is a child process relative to main() */ 

int main(int argc, char * argv[]) { 
    if(argc < 2){ 
     printf("Usage: %s option (option) ...\n", argv[0]); 
     exit(1); 
    } 
    pipeline(argv, 1, STDIN_FILENO); 
    return 0; 
} 

void error_exit(const char *kom){ 
    perror(kom); 
    (child ? _exit : exit)(EXIT_FAILURE); 
} 

void pipeline(char *ar[], int pos, int in_fd){ 
    if(ar[pos+1] == NULL){ /*last command */ 
     if(in_fd != STDIN_FILENO){ 
      if(dup2(in_fd, STDIN_FILENO) != -1) 
       close(in_fd); /*successfully redirected*/ 
      else error_exit("dup2"); 
     } 
     execlp(ar[pos], ar[pos], NULL); 
     error_exit("execlp last"); 
    } 
    else{ 
     int fd[2]; 
     pid_t childpid; 

     if ((pipe(fd) == -1) || ((childpid = fork()) == -1)) { 
      error_exit("Failed to setup pipeline"); 
     } 
     if (childpid == 0){ /* child executes current command */ 
      child = 1; 
      close(fd[0]); 
      if (dup2(in_fd, STDIN_FILENO) == -1) /*read from in_fd */ 
       perror("Failed to redirect stdin"); 
      if (dup2(fd[1], STDOUT_FILENO) == -1) /*write to fd[1]*/ 
       perror("Failed to redirect stdout"); 
      else if ((close(fd[1]) == -1) || (close(in_fd) == - 1)) 
       perror("Failed to close extra pipe descriptors"); 
      else { 
       execlp(ar[pos], ar[pos], NULL); 
       error_exit("Failed to execlp"); 
      } 
     } 
     close(fd[1]); /* parent executes the rest of commands */ 
     close(in_fd); 
     pipeline(ar, pos+1, fd[0]); 
    } 
} 

它適用於高達3個命令完全沒問題,但是當涉及到4,更它不更多和分析了幾個小時之後,我仍然無法得到地方問題是。

Example: 
./prog ls uniq sort head 

gives: 
sort: stat failed: -: Bad file descriptor 
+0

「真」情況下,最後一個'if'子句('childpid == 0')會發生什麼?那條後面的最後三行不應該在'else'塊中? – krlmlr

+0

krlmlr:是我的想法之前,在把它放在else塊之後並不會改變任何東西 – Mia

回答

5

不是專家,但似乎以下行的問題是:

((close(fd[1]) == -1) || (close(in_fd) == - 1)) 

儘量不要靠近in_fd那裏。

我想,父母正試圖關閉由孩子關閉的同一個fd。
當您使用dup2()時,您不需要關閉文件,因爲dup2()已經關閉了該文件。

+0

我能夠複製@ Mia的問題,併成功應用這個建議。 – mah

+0

哇,謝謝!它是如此簡單。 – Mia

+0

來自[後續問題](http://stackoverflow.com/questions/21332970/pipe-with-fork-with-recursion-file-descriptors-handling/21333381?noredirect=1#comment32160059_21333381),我在這裏不明白一件事:我找不到'man'頁面中說'dup2()'關閉* first *參數的地方;它會嘗試關閉第二個,即'STDOUT_FILENO'或'STDIN_FILENO'。 'fd [1]'和'in_fd'仍應該打開。手冊頁部分:「int dup2(int oldfd,int newfd); [...] dup2()使newfd成爲oldfd的副本,必要時關閉newfd ......」 – Evert