C 当程序关闭并且数据从tail-f通过管道传输时,为什么fgets挂起?

C 当程序关闭并且数据从tail-f通过管道传输时,为什么fgets挂起?,c,pipe,stdin,fgets,tail,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

我正在使用
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;
}
//gcc app.c-lncurses
//tail-f日志文件|/a.out
#包括
#包括
#包括
#包括
无效打印状态(窗口*win,常量字符*str){
初始对(1,红色,白色);
清除();
mvwhline(win,0,0,,,COLS);
mvwaddnstr(win、0、0、str、COLS);
雷弗斯(赢);
}
无效打印行(窗口*win,常量字符*str,整数*i){
初始对(2,红色,黑色);
如果(*i>行-2){
*i=1;
wclear(win);
}
mvwprintw(win,(*i)+,0,str);
雷弗斯(赢);
}
int main(){
窗口*menu_win,*wi;
int i=1,q=1,fd1,fd2,是_atty=1;
char-buf[1024];
initscr();
清除();
半延迟(5);
menu_win=newwin(1,COLS,0,0);
wi=newwin(行-1,COLS,1,0);
键盘(菜单,正确);
文件*input=stdin;
如果(!isatty(fileno(stdin))){
is_atty=0;
fd1=打开(“/dev/tty”,仅限Ordu);
fd2=dup(文件号(标准输入));
输入=fdopen(fd2,“r”);
freopen(“/dev/tty”,“r”,stdin);
如果(文件号(标准输入)!=0)/*来自ncurses对话框*/
(无效)dup2(文件号(标准输入),0);
关闭(fd1);
int flags=fcntl(fd2,F_GETFL);
//非阻塞FGET,因此getch工作正常
fcntl(fd2、F|U设置FL、标志| O|U非块);
}
打印状态(菜单“使用箭头键上下移动”);
while(q){
开关(wgetch(菜单_-win)){
案例编号:
打印状态(菜单“上移”);
打破
案例键向下:
打印状态(菜单“向下移动”);
打破
案例‘q’:
q=0;
打破
违约:
如果(正在)中断;
while(fgets(buf,1024,输入)!=NULL){
打印行(wi、buf和i);
}
打破
}
}
fclose(输入);
关闭(fd2);
endwin();
返回0;
}

一旦您的代码调用
fgets()
它将在
fgets()
中保持阻塞状态,直到更多输入到达-或管道关闭

这不会发生在
cat somelog |/a.out
上,因为正如您所怀疑的那样,
cat
没有挂起等待更多数据写入文件。
cat
继续发送数据,然后退出,关闭管道并导致
fgets()
返回缓冲的数据并等待更多的数据,或者如果没有读取数据,返回
NULL
tail-f…
在到达文件末尾时不会退出并关闭管道-
tail-f…
只会等待更多数据写入正在跟踪的文件,因此您对
fgets()的调用
就坐在那里

那么为什么呢?因为
fgets()
在获得换行符或EOF之前不会返回:

fgets()
从中最多读取一个小于大小的字符 流动 并将其存储到s指向的缓冲区中。读取在
EOF
换行符


一旦您的代码调用了
fgets()
,它将在
fgets()
中保持阻塞状态,直到更多输入到达-或者管道关闭

这不会发生在
cat somelog |/a.out
上,因为正如您所怀疑的那样,
cat
没有挂起等待更多数据写入文件。
cat
继续发送数据,然后退出,关闭管道并导致
fgets()
返回缓冲的数据并等待更多的数据,或者如果没有读取数据,返回
NULL
tail-f…
在到达文件末尾时不会退出并关闭管道-
tail-f…
只会等待更多数据写入正在跟踪的文件,因此您对
fgets()的调用
就坐在那里

那么为什么呢?因为
fgets()
在获得换行符或EOF之前不会返回:

fgets()
从中最多读取一个小于大小的字符 流动 并将其存储到s指向的缓冲区中。读取在
EOF
换行符


cat
发送一个
EOF
,而
tail-f
不发送,它只是保持管道打开。如果它是
tail-100
,它的行为将类似于
cat
。但是,当你按
q
时,你识别的行不会被调用。你确实关闭了
input
,但你没有关闭
fd2
@alvits谢谢编辑编辑问题并添加了
close(fd2)
默认值:
案例仅在您按任意键时执行。当您按
q
时,它执行
case'q':
,随后导致
while
循环终止。它不是
fgets()
这导致它阻塞。管道在到达
返回0时仍然打开;
。运行
tail-f somelog | strace./a.out
。查看您的代码是否没有关闭管道。
tail
将在收到
SIGPIPE
时终止。记住,您有多个管道副本。在
案例“q”中打印一些内容:
block。这将有助于您进行故障排除。@谢谢您的回复。进行一次strace,我看到
q
存在循环并达到
close(fs2)
,我还看到
fd1=4
fd2=5
,然后关闭
4
,然后关闭
0
,最后关闭