我正在使用Ubuntu Linux 12.04并编写一个使用ncurses的程序。我的程序有一个执行从属进程的选项(“shell转义”)。在创建从属流程之前我做了
reset_shell_mode( );
putp( exit_ca_mode ); // From <term.h>
然后当从属进程退出时,我用
恢复我的curses显示putp( enter_ca_mode ); // From <term.h>
reset_prog_mode( );
refresh( );
这很好用。但是,我的程序还希望在启动从属进程之前输出一些信息。它还希望在从属进程退出但在返回完整的curses显示之前输出一些附加信息。因此我有(缩写):
reset_shell_mode( );
putp( exit_ca_mode );
printf( "Don't forget... blah, blah\n" );
system( external_command );
printf( "Updating, etc\n" );
putp( enter_ca_mode );
reset_prog_mode( );
refresh( );
问题是我的程序在调用system()之前和之后产生的文本没有出现。我想也许它仍然会进入一些与curses相关的缓冲区。我不知道。
如何让父进程也在终端以及子进程上输出?
答案 0 :(得分:1)
Curses保留自己的缓冲区,了解屏幕应该是什么样子。当你调用refresh()时,它调整屏幕以匹配该缓冲区,这意味着curses不知道的所有内容都将被覆盖(*)。
printf,以及任何外部命令的输出,绕过该缓冲区,直接进入屏幕(更确切地说,是标准输出,恰好连接到屏幕,因为它们从shell继承了它们的标准输出)。
因此,要将printf输出转换为curses,需要将printf替换为printw。要将其他程序的输出转换为curses,您必须将其输出捕获到程序中,然后将其提供给curses。
执行此操作的简单方法是将输出重定向到文件,然后读取文件:
system("ls > tempfile");
if ((fp=fopen("tempfile", "r"))!=NULL) {
while (fgets(buf, sizeof buf, fp))
printw("%s", buf);
fclose(fp);
}
警告:这个例子被大量删除,为您提供一个想法。它没有很好地捕获错误,它使用容易出现各种缓冲区溢出的fgets,并且它使用临时文件的常量名称,这会导致很多并发问题。
更好的方法是在流程和您尝试运行的程序之间创建管道:
int p[2];
pipe(p);
if (fork()==0) { // child process
close(1);
dup(p[1]);
close(p[1]);
close(p[0]);
execlp("ls", "ls", NULL);
} else { // parent process
close(p[1]);
if ((fp=fdopen(p[0], "r"))!=NULL) {
while (fgets(buf, sizeof buf, fp))
printw("%s", buf);
fclose(fp);
}
}
同样,这个例子被大量删除(我直接在浏览器中键入它,从未编译或运行它)。要真正理解它,并添加所有缺少的错误检查,了解linux / unix进程模型,管道,文件描述符与C文件指针 - 那里有很多教程,这远远超出了你原来的问题。
但是,总结一下:如果你想让curses在屏幕上放置任何东西,你必须使用适当的curses函数。一旦curses刷新屏幕,绕过curses的所有东西都可能被覆盖。
(*)如果curses认为屏幕和内部缓冲区之间只有区别,它只会更新不同的字符,而不是整个屏幕。因此,如果您的外部程序写入屏幕的部分屏幕,而诅咒认为不需要更新,则会将这些部分单独保留,这意味着程序输出的一部分将保留。
答案 1 :(得分:0)
在示例中
reset_shell_mode( );
putp( exit_ca_mode );
printf( "Don't forget... blah, blah\n" );
system( external_command );
printf( "Updating, etc\n" );
putp( enter_ca_mode );
reset_prog_mode( );
refresh( );
reset_shell_mode()
来电尝试恢复终端设置。有一个问题。 curses(一般来说,不仅仅是ncurses)将终端模式设置为“raw”(允许您在不受I / O缓冲干扰的情况下读取输入字符),但还设置输出缓冲(表现)。
它使用setvbuf
的某些变体执行此操作,根据标准 无法 可靠地关闭/打开:
在流指向的流与打开的文件相关联之后可以使用
setvbuf()
函数,但在之前 任何其他操作(不成功的除外)呼叫setvbuf()
)在流上执行。
这不仅仅是一个很好的细节;如果你试图放弃缓冲,一些实现转储核心。所以ncurses保持一致。但同样需要注意的是:
putp
使用相同的输出缓冲,但printw
使用一个单独的缓冲区,在其重绘操作(例如refresh
)期间进行刷新。在任何一种情况下,此示例的修复方法是在使用与前一次调用不同的输出流时使用fflush
。这应该有效:
reset_shell_mode( );
putp( exit_ca_mode );
printf( "Don't forget... blah, blah\n" );
fflush(stdout); // added
system( external_command );
printf( "Updating, etc\n" );
putp( enter_ca_mode );
fflush(stdout); // added
reset_prog_mode( );
refresh( );