为什么fgets在程序关闭时挂起,而数据从tail -f传输?

时间:2017-02-25 01:51:01

标签: c pipe stdin fgets tail

我正在使用tail -f读取日志并将数据传递给我的ncurses程序。

tail -f somelog | ./a.out

它工作正常,但是当我按 q 时,程序不会退出,只是挂起直到将一个字节写入正在尾随的文件。使用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;
}

1 个答案:

答案 0 :(得分:0)

一旦您的代码调用fgets(),它将在fgets()中被阻止,直到更多输入到达 - 或者管道已关闭。

cat somelog | ./a.out不会发生这种情况,因为正如您所怀疑的那样,cat并没有等待更多数据写入文件。 cat不断发送数据然后退出,关闭管道并导致fgets()返回已缓冲的数据并等待更多数据,如果没有数据则NULL已被阅读。当tail -f ...到达文件末尾时,tail -f ...不会退出并关闭管道 - fgets()只是等待更多数据写入它正在拖尾的文件,所以你的电话到fgets()只是坐在那里。

为什么?因为fgets()在获得换行符或EOF之前不会返回。 Per the Linux man page (assuming you're running on Linux)

  

EOF最多只读取一个 size 字符             并将它们存储到 s 指向的缓冲区中。阅读停止后          newlineStudent。 ...