2017-02-25 33 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