2013-07-26 23 views
0

我目前正試圖理解dup2和C管道的合併,但是即使最簡單的程序似乎也不行。已經在閱讀示例代碼時,我很困惑他們什麼時候關閉管道的末端以及輸出應該在哪裏打印。使用dup2和pipe時,必須訂購什麼東西?

有時write end is closed,即使應該產生一行後面的輸出應該進入管道。在其他例子中,the unused end is closed(這對我更有意義)。

然後,我不明白dup2何時應該執行。我想它應該變成之前我想要重定向的輸出,但我覺得我今天也看到了不同的感覺。

所以最後我想出了這個小測試,每行都有printffflush,沒有任何東西通過管道重定向。爲什麼?我究竟做錯了什麼?

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

int main(void) { 
    int out_pipe[2]; 
    char *output[101]; 

    if (pipe(out_pipe) != 0) { 
     perror("pipe()"); 
     exit(1); 
    } 

    printf("Hello"); 
    fflush(stdout); 

    dup2(out_pipe[1], STDOUT_FILENO); 
    printf("Hello"); 
    fflush(stdout); 

    close(out_pipe[1]); 
    printf("Hello"); 
    fflush(stdout); 

    read(out_pipe[0], output, 100); 
    close(out_pipe[0]); 

    printf("PIPE: %s", output); 
    fflush(stdout); 

    return 0; 
} 

回答

4
  1. 結束您printf()消息用換行; fflush()仍然是一個好主意,因爲您即將更改標準輸出的位置,但如果程序的標準輸出傳送到終端通常不是必需的。如果標準輸出正在進行文件處理,並且fflush()未到位,那麼您將得到三份"Hello\n"寫入管道的副本。

  2. 當您將標準輸出更改爲管道時,您的消息確實被寫入管道。

  3. 關閉寫入文件描述符時,不會遇到任何問題。然後你寫第二個Hello到管道。您需要這個fflush()以確保標準I/O包實際上已將其緩衝數據寫入管道。

  4. 然後您從管道讀入緩衝區output。您應該檢查您讀取的字節數,因爲字符串不會以null結尾。您應該讀取10個字節(當消息中沒有任何換行符時)。

  5. 然後用PIPE:前綴再次寫入管道。

要修復,請將消息寫入標準錯誤。

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

int main(void) 
{ 
    int out_pipe[2]; 
    char output[101]; 

    if (pipe(out_pipe) != 0) { 
     perror("pipe()"); 
     exit(1); 
    } 

    printf("Hello\n"); 
    fflush(stdout); 

    dup2(out_pipe[1], STDOUT_FILENO); 
    printf("Hello\n"); 
    fflush(stdout); 

    close(out_pipe[1]); 
    printf("Hello\n"); 
    fflush(stdout); 

    int n = read(out_pipe[0], output, sizeof(output)); 
    close(out_pipe[0]); 

    fprintf(stderr, "PIPE: %.*s\n", n, output); 

    return 0; 
} 

注意,我改變的output定義從char *數組的char一個簡單的數組。有了變化,我得到的輸出:

$ ./pipe3 
Hello 
PIPE: Hello 
Hello 

$ 

那是因爲我包含在寫入管道消息換行,以及在標準錯誤結束的格式字符串。


有沒有一種可能,以 「重新啓用」 標準輸出?

是的;使用dup2()之前簡單地保持標準輸出原始的文件描述符的副本,然後恢復副本,一旦你與管道完成。

我已經刪除了兩個主要fflush()電話,和樣品輸出演示端和輸出文件之間的差異:

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

int main(void) 
{ 
    int out_pipe[2]; 
    char output[101]; 
    int old_stdout; 

    if (pipe(out_pipe) != 0) { 
     perror("pipe()"); 
     exit(1); 
    } 

    printf("Hello\n"); 

    old_stdout = dup(STDOUT_FILENO); 

    dup2(out_pipe[1], STDOUT_FILENO); 
    printf("Hello\n"); 

    close(out_pipe[1]); 
    printf("Hello\n"); 
    fflush(stdout); 

    int n = read(out_pipe[0], output, sizeof(output)); 
    close(out_pipe[0]); 

    dup2(old_stdout, STDOUT_FILENO); 
    printf("PIPE: %d <<%.*s>>\n", n, n, output); 

    return 0; 
} 

樣品輸出:

$ ./pipe3Hello 
PIPE: 12 <<Hello 
Hello 
>> 
$./pipe3 > output 
'pipe3' is up to date. 
$ cat output 
PIPE: 18 <<Hello 
Hello 
Hello 
>> 
$ 

如果去除剩餘的fflush() ,程序掛起。有沒有在管(因爲標準I/O並沒有刷新其緩衝區,因爲它不是完整的,輸出不是終端更多),但管道是對寫開放,所以內核認爲輸入可能會出現在它上面—如果只有管道打開的程序未在管道的讀取端等待輸入顯示。該計劃本身已陷入僵局。

+0

是啊,這工作。是否有可能「重新啓用」stdout?其實我試圖在使用ncurses的時候存儲一些stdout消息(它會忽略這些消息,因爲它使用stdout本身)。所以我想把這些消息發送給一個字符串,然後用'stradd()'將它們發佈到ncurses。 – Aufziehvogel

+0

我剛想到的一個想法是分叉,然後等待stdout和管道回到原始程序。我認爲這應該起作用,因爲原始線程仍然具有標準輸出,不是嗎?在那裏,我可以從管道的讀端讀取它。 – Aufziehvogel

+0

如果你使用'fork()',你有兩個進程(每個進程只有一個控制線程),而不是你的註釋所暗示的一個進程中的兩個線程。是的,您可以在創建管道之後進行分叉,讓子進行I/O重定向並將管道中的信息寫入父級,然後讓父級讀取該數據並使用它。謹慎對待詛咒,雖然—,它聽起來很可疑,好像你可能會給它半無法消化的數據來顯示,這可能破壞它對屏幕上什麼位置的理解。 –

相關問題