2012-08-26 22 views
0

我寫了一個使用多個管道模擬shell的C程序。問題是我可以運行大部分命令,如ls | cat等,但我無法使用ls | wcwc不能正常工作嗎?ls |使用C的wc不起作用

int pipefd[4]; 
int p1 = pipe(pipefd);   // Open pipe 1 
int p2 = pipe(pipefd + 2);  // Open pipe 2 

pid_t pid; 

for(i = 0; i < n_commands; i++) 
{ 
    fflush(stdout); 
    pid = fork(); 

    if(pid == 0) 
    { 
     int command_no = i; 
     int prev_pipe = ((command_no - 1) % 2) * 2; 
     int current_pipe = (command_no % 2) * 2; 

     // If current command is the first command, close the 
     // read end, else read from the last command's pipe 
     if(command_no == 0) 
     { 
      close(pipefd[0]); 
     } 
     else 
     { 
      dup2(pipefd[prev_pipe], 0); 
      close(pipefd[current_pipe]); 
     } 

     // If current command is the last command, close the 
     // write end, else write to the pipe 
     if(command_no == n_commands - 1) 
     { 
      close(pipefd[current_pipe + 1]); 
     } 
     else 
     { 
      dup2(pipefd[current_pipe + 1], 1); 
     } 

     int p = execvp(tokens[cmd_pos[command_no]], tokens + cmd_pos[command_no]); 

     close(pipefd[current_pipe]); 
     close(pipefd[prev_pipe]); 
     close(pipefd[prev_pipe + 1]); 
     close(pipefd[current_pipe + 1]); 

     _exit(0); 
    } 
} 

看來,如果他們不是在流水線的第一個命令不被執行從/usr/bin的程序。

+0

當您嘗試從「shell」C程序中調用此命令時,您遇到此問題嗎?怎麼了? – Levon

+0

是的,在C程序中。什麼都不會發生,'execvp()'不會返回任何錯誤。 – green

+1

你能展示一些代碼嗎? – cnicutar

回答

1

下面是你的代碼創建了一個非常簡單的程序 - 猜管道如何可能會創建並簡化命令argv處理了一下:

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

static char *argv_ls[] = { "ls", 0 }; 
static char *argv_wc[] = { "wc", 0 }; 
static char **cmds[] = { argv_ls, argv_wc }; 

int main(void) 
{ 
    int n_commands = 2; 
    int pipefd[2]; 

    pipe(&pipefd[0]); // Error check! 

    fflush(stdout); 
    for (int i = 0; i < n_commands; i++) 
    { 
     int pid = fork(); 

     if (pid == 0) 
     { 
      int command_no = i; 
      int prev_pipe = ((command_no - 1) % 2) * 2; 
      int current_pipe = (command_no % 2) * 2; 
      printf("cmd %d: prev pipe %d, curr pipe %d\n", i, prev_pipe, current_pipe); 
      fflush(stdout); 

      // If current command is the first command, close the 
      // read end, else read from the last command's pipe 
      if (command_no == 0) 
      { 
       close(pipefd[0]); 
      } 
      else 
      { 
       dup2(pipefd[prev_pipe], 0); 
       close(pipefd[current_pipe]); // Line 40 
      } 

      // If current command is the last command, close the 
      // write end, else write to the pipe 
      if (command_no == n_commands - 1) 
       close(pipefd[current_pipe + 1]); // Line 46 
      else 
       dup2(pipefd[current_pipe + 1], 1); 

      execvp(cmds[i][0], cmds[i]); 
      fprintf(stderr, "Failed to exec: %s (%d: %s)\n", cmds[i][0], errno, strerror(errno)); 
      _exit(1); 
     } 
    } 

    return 0; 
} 

當GCC 4.7.1(在Mac OS X 10.7.4)編譯它,它警告說:

pipes-12133858.c: In function ‘main’: 
pipes-12133858.c:40:22: warning: array subscript is above array bounds [-Warray-bounds] 
pipes-12133858.c:46:22: warning: array subscript is above array bounds [-Warray-bounds] 

當我運行它,我得到的輸出:

Isis JL: pipes-12133858 
cmd 0: prev pipe -2, curr pipe 0 
cmd 1: prev pipe 0, curr pipe 2 
Isis JL: wc: stdin: read: Bad file descriptor 

由於代碼中的父代不會等待孩子完成,所以在出現wc錯誤消息之前會出現提示,但打印的診斷號顯示存在各種問題(並且編譯器能夠發現一些問題問題)。

請注意,不需要檢查任何exec*()函數系列的返回值。如果他們成功了,他們就不會回來;如果他們回來,他們失敗了。在致電_exit(0);之前,也不需要關閉系統,因爲系統無論如何都會關閉它們。另外,當你不能執行某些事情時,打印一條消息指出你未能執行的消息並以非零退出狀態退出是有禮貌的。

因此,正如Michał Górny所說,你的問題的主要部分是你的管道代碼至少是神祕的,因爲你沒有顯示它,可能是錯誤的。

我也很確定你沒有足夠的close()調用你的代碼。作爲指導原則,在每個打開了管道並將成爲管道一部分的進程中,在給定子進程使用exec*()函數之前,系統調用返回的所有文件描述符都應該關閉。由於管道的寫入端已打開,因此不關閉管道可能會導致進程掛起。如果打開寫入結束的進程是試圖從管道的讀取端讀取的進程,那麼它將不會找到任何要讀取的數據。

+0

行'int command_no = i'存在一些問題。你知道我怎樣才能訪問'我'? (不使用共享內存) – green

+0

@ green7:有?父母和孩子中'i'的值是相同的。在第一次迭代中,'i'和因此'command_no'的值是0;與打印值一樣,「(0-1)%2」爲「-1」,「2 * -1」爲「-2」。等等。也許你不知道負數和正數的模數是負數還是零? –

+0

哦,沒錯。 :)另外,'prev_pipe'的值在第一次迭代中並不重要,因爲有代碼檢查命令是否是第一個命令。此外,還有兩個管道:'pipefd [0,1]'和'pipefd [2,3]'。第一個命令在'current_pipe + 1 = 1'中寫入,下一個命令從'prev_pipe = 0'讀取,寫入'stdout'或'current_pipe + 1 = 3'。 – green

1

您正在錯誤連接管道。

這個邏輯:

int prev_pipe = ((command_no - 1) % 2) * 2; 
int current_pipe = (command_no % 2) * 2; 

不起作用 - 模的結果總是01,所以prev_pipecurrent_pipe將是要麼02 ...

那麼,除非我錯過了一些隱藏的概念,因爲你沒有粘貼任何創建管道的代碼。

+0

我認爲一般的想法是,如果你有N個命令,將會在單個整數數組中創建N-1個管道:'int pipefd [2 * MAX_PIPELINE]; for(int i = 0; i

+0

正如我所說,它支持多個管道。我已經聲明瞭兩個管道'pipefd [0,1]'和'pipefd [2,3]',不管有多少管道,它都會處理通信。 – green