2017-02-25 34 views
1

我正在使用tail -f讀取日誌並將數據傳輸到我的ncurses程序。爲什麼fgets在程序關閉並且數據從tail -f傳送時掛起?

tail -f somelog | ./a.out 

當我按下q它的工作原理,除了罰款,程序不退出,它只是掛起直到一個字節寫入文件是tail'd。這在使用cat somelog | ./a.out時不會發生,我想這是因爲沒有被遵循。

它看起來像行while(fgets(buf, 1024, input)是造成這個問題。任何想法爲什麼以及如何修復它?由於

// gcc app.c -lncurses 
// tail -f logfile | ./a.out 

#include <stdio.h> 
#include <ncurses.h> 
#include <fcntl.h> 
#include <string.h> 

void print_status(WINDOW * win, const char *str) { 
    init_pair(1, COLOR_RED, COLOR_WHITE); 
    clear(); 
    mvwhline(win, 0, 0, ' ', COLS); 
    mvwaddnstr(win, 0, 0, str, COLS); 
    wrefresh(win); 
} 

void print_line(WINDOW * win, const char *str, int *i) { 
    init_pair(2, COLOR_RED, COLOR_BLACK); 
    if (*i > LINES - 2) { 
     *i = 1; 
     wclear(win); 
    } 
    mvwprintw(win, (*i)++, 0, str); 
    wrefresh(win); 
} 

int main() { 
    WINDOW *menu_win, *wi; 
    int i = 1, q = 1, fd1, fd2, is_atty = 1; 
    char buf[1024]; 

    initscr(); 
    clear(); 
    halfdelay(5); 

    menu_win = newwin(1, COLS, 0, 0); 
    wi = newwin(LINES - 1, COLS, 1, 0); 
    keypad(menu_win, TRUE); 

    FILE *input = stdin; 

    if(!isatty(fileno(stdin))) { 
     is_atty = 0; 
     fd1 = open("/dev/tty", O_RDONLY); 
     fd2 = dup(fileno(stdin)); 
     input = fdopen(fd2, "r"); 
     freopen("/dev/tty", "r", stdin); 
     if(fileno(stdin) != 0) /* from ncurses dialog */ 
      (void)dup2(fileno(stdin), 0); 
     close(fd1); 
     int flags = fcntl(fd2, F_GETFL); 
     // non-blocking fgets so getch works fine 
     fcntl(fd2, F_SETFL, flags|O_NONBLOCK); 
    } 

    print_status(menu_win, "Use arrow keys to go up and down"); 
    while(q) { 
     switch(wgetch(menu_win)) { 
      case KEY_UP: 
       print_status(menu_win, "Moved up"); 
       break; 
      case KEY_DOWN: 
       print_status(menu_win, "Moved down"); 
       break; 
      case 'q': 
       q = 0; 
       break; 
      default: 
       if(is_atty) break; 
       while(fgets(buf, 1024, input) != NULL) { 
        print_line(wi, buf, &i); 
       } 
       break; 
     } 
    } 
    fclose(input); 
    close(fd2); 
    endwin(); 
    return 0; 
} 
+0

'cat'發送'EOF',而'tail -f'沒有,它只是保持管道打開。如果是「尾巴-100」,它將表現得像貓一樣。但是,當您按'q'時,您標識的行不會被調用。你沒有關閉'input',但你沒有關閉'fd2'。 – alvits

+0

@alvits謝謝我編輯了這個問題並添加了'close(fd2)'。 – user1024718

+0

'default:'情況只有在您按任意鍵時纔會執行。當你按下'q'時,它執行'case'q':',隨後導致'while'循環終止。這不是導致它阻塞的'fgets()'。當它到達'return 0;'時,管道仍然打開。運行'tail -f somelog | strace。/ a.out'。看看你的代碼是否關閉管道。當它收到一個SIGPIPE時,tail會終止。請記住,你有幾個管道副本。在'case'q'中打印一些東西:'block。這將幫助您排除故障。 – alvits

回答

0

一旦你的代碼調用fgets()將在fgets()保持阻塞,直到更多的輸入到達 - 或者在管道關閉。

這不會發生在cat somelog | ./a.out上,因爲如您懷疑的那樣,cat並未等待更多數據寫入文件。 cat保持發送數據然後退出,關閉管道並導致fgets()返回緩衝的數據並等待更多數據,或者如果沒有數據被讀取,則返回NULLtail -f ...當它碰到文件末尾時不會退出並關閉管道 - tail -f ...只是等待將更多數據寫入到正在拖尾的文件中,因此您只需撥打fgets()即可。

那麼爲什麼?因爲fgets()在獲得換行符或EOF之前不會返回。 Per the Linux man page (assuming you're running on Linux)

fgets()在至多一個小於尺寸從 ,並將它們存儲字符讀取到緩衝區指向小號。讀數在 和EOFnewline之後停止。 ...

+0

不會調用'O_NONBLOCK'使'fgets'返回嗎?有沒有辦法來解決這個問題?謝謝 – user1024718

+0

@ user1024718我認爲這將取決於實施。在FILE *下面弄亂文件描述符屬性似乎有問題。 'fgets()'應該阻塞到'EOF'或換行符。請參閱http://stackoverflow.com/questions/6055702/using-fgets-as-non-blocking-function-c –

+0

這是一個好點,雖然,我有點猶豫加入select(),因爲它會阻止和我正在使用'getch()'讓鍵盤工作。 – user1024718