簡體   English   中英

在C中實現Shell-流水線輸入具有正確的輸出,但退出循環

[英]Implementing shell in C - pipelined input has correct output but exits loop

我正在嘗試在C中實現一個可處理多個管道的基本shell。 它等待輸入並在for循環中執行命令。 收到EOF時,它將停止等待輸入並退出。

現在,當我輸入管道命令時,例如ls | wc | grep ... shell會輸出正確的輸出ls | wc | grep ... ls | wc | grep ... ls | wc | grep ...它停止等待輸入,並退出外部while循環,而不是等待下一行輸入。

我發現發生這種情況是因為while循環中的fget返回null(stdin以某種方式得到EOF嗎?)。 創建派生,創建管道或執行時,我沒有任何錯誤。

但是,如果我一次輸入一個命令而沒有任何管道(例如ls ,它將成功打印出正確的輸出, 並按需要等待下一行輸入。

我的程序在嘗試執行每個命令之前將輸入的每一行解析為一個struct (在下面省略)。 設計該struct是為了使我可以輕松將解析后的參數傳遞給execvp ,在此不再贅述。

這是我的代碼的簡化版本,省略了大多數錯誤檢查:

FILE* input;
char line[MAX_LINE];

input = stdin;
printf("> ");
fflush(stdout);

while (fgets(line, sizeof(line), input)) {
    int i;
    struct cmdLine;
    /* struct defined elsewhere
    ** commands = # of commands in parsed input
    ** start = index where a command and its args start
    ** args[] = array holding each command/arg
    */

    /* parse input line into cmdLine */
    ...

    /* exec all commands in pipeline except the last */
    for (i = 0; i < cmdLine.commands-1; ++i) {
        int pd[2];
        pipe(pd);

        if (fork() == 0) {
            dup2(pd[1], 1);
            execvp(cmdLine.args[cmdLine.start[i]], &(cmdLine.args[cmdLine.start[i]]));
        } else {
            wait(NULL);
        }

        dup2(pd[0], 0);
        close(pd[1]);
    }    

    /* exec last command */
    if (fork() == 0) {
        execvp(cmdLine.args[cmdLine.start[i]], &(cmdLine.args[cmdLine.start[i]]));
    } else {
        wait(NULL);
    }

    if (stdin == input) {
        printf("> ");  /* print shell prompt */
        fflush(stdout);
    }
}

幾乎可以肯定,我在欺騙某個地方弄得一團糟,但是我已經嘗試了好幾個小時,而且我不明白自己在做什么錯。 EOF是否以某種方式發送到stdin,以便封閉的fgets返回NULL

通過使用0(= stdin )作為第二個參數調用dup2 ,您將在for循環的每次迭代結束時關閉原始的stdin ,因此您不再可以通過原始的stdin與您的程序進行實際交談。

代碼中的問題是,您試圖將所有管道連接在一起並傳遞給其他人。 那是行不通的。 這是應該起作用的方法:

  • 對於n個程序,您至少需要(n-1)個管道。
  • 將所有管道FD記錄為數組:一個用於管道的輸入側(即被寫入),一個用於輸出側(即從中讀取)。
  • 對於您要分叉的每個進程,請將前一個管道的輸出(如果有)連接到其stdin ,並將下一個管道的輸入連接到其stdout (如果要處理管道鏈中的最后一個進程,則將其連接到主程序的stdout )。
  • 分叉所有內容后:在循環中,對管道的輸出FD上的poll()進行讀取,從具有活動的任何管道上讀取,然后寫入下一個管道的輸入(最后是您自己的stdout )。 如果在其中一個管道上得到EOF,請關閉下一個管道的輸入(並從輸出數組中刪除EOF管道輸出)。 一旦所有FD關閉,退出循環。

編輯:我只是想到了另一種更簡單的方法,它需要更少的代碼更改,但我還沒有完全考慮。 :)問題在於您正在銷毀自己的標准輸入。 如果您在派生的子級中執行所有這些操作(即整個“處理一行命令”),則在進程之間替換stdin根本不會影響父進程。但是,這仍然需要內核中的大量緩沖,因此它可能不會擴展。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM