【发布时间】:2017-07-16 00:24:51
【问题描述】:
我正在使用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;
}
【问题讨论】:
-
cat发送一个EOF而tail -f没有,它只是保持管道打开。如果它是tail -100,它将表现得像cat。但是,当您按q时,不会调用您识别的行。你确实关闭了input,但你没有关闭fd2。 -
@alvits 谢谢我编辑了问题并添加了
close(fd2)。 -
default:case 仅在您按任意键时执行。当您按下q时,它会执行case 'q':,这随后会导致while循环终止。导致它阻塞的不是fgets()。管道到达return 0;时仍处于打开状态。运行tail -f somelog | strace ./a.out。查看您的代码是否没有关闭管道。tail将在收到SIGPIPE时终止。请记住,您有多个管道副本。在case 'q':块中打印一些内容。这将帮助您进行故障排除。 -
@alvits 感谢您的回复。做一个strace,我看到
q存在循环并到达close(fs2),我还看到fd1 = 4和fd2 = 5,然后关闭4,然后关闭0,最后关闭5.我看不到哪个 fd 仍然打开。我忽略了什么吗? -
您的问题确实没有任何解决方案。以
tail -f /etc/hosts | ls或tail -f /etc/hosts | /bin/echo ok为例,两者都会挂起,直到您终止tail -f。简而言之,当管道的写入端仍然打开时,读取器将退出,但控制权不会返回到外壳,直到您终止生产者。而straceing 2个样本说明进程已经成功退出,exit_group(0) = ? +++ exited with 0 +++,只有tail -f没有。