如何使用 ncurses 在窗口中捕获命令行输出?
假设我正在执行像“ls”这样的命令,我想在 ncurses 中设计的特定窗口中打印该输出。我是 ncurses 的新手。帮助我。在此先感谢
如何使用 ncurses 在窗口中捕获命令行输出?
假设我正在执行像“ls”这样的命令,我想在 ncurses 中设计的特定窗口中打印该输出。我是 ncurses 的新手。帮助我。在此先感谢
更优雅的解决方案可能是在您的程序中实现重定向。查看 dup() 和 dup2() 系统调用(参见 dup(2) 联机帮助页)。所以,你想要做的是(这本质上就是 system() 调用的 shell 最终会做的事情):
代码片段:
char *tmpname;
int tmpfile;
pid_t pid;
int r;
tmpname = strdup("/tmp/ls_out_XXXXXX");
assert(tmpname);
tmpfile = mkstemp(tmpname);
assert(tmpfile >= 0);
pid = fork();
if (pid == 0) { // child process
r = dup2(STDOUT_FILENO, tmpfile);
assert(r == STDOUT_FILENO);
execl("/bin/ls", "ls", NULL);
assert(0);
} else if (pid > 0) { // parent
waitpid(pid, &r, 0);
/* you can mmap(2) tmpfile here, and read from it like it was a memory buffer, or
* read and write as normal, mmap() is nicer--the kernel handles the buffering
* and other memory management hassles.
*/
} else {
/* fork() failed, bail violently for this example, you can handle differently as
* appropriately.
*/
assert(0);
}
// tmpfile is the file descriptor for the ls output.
unlink(tmpname); // file stays around until close(2) for this process only
对于更挑剔的程序(那些关心它们是否有输入和输出终端的程序),您需要查看伪 tty,请参阅 pty(7) 联机帮助页。(或 google 'pty'。)如果您希望 ls 进行多列漂亮打印(例如, ls 将检测到它正在输出到文件,并将一个文件名写入一行。如果您希望 ls 做辛苦你了,你需要一个 pty。此外,你应该能够在 fork() 之后设置 $LINES 和 $COLUMNS 环境变量,以让 ls 漂亮地打印到你的窗口大小——再次,假设你正在使用 pty。基本的变化是您将删除 tmpfile = mkstemp(...); 行并将其和周围的逻辑替换为 pty 打开逻辑并扩展 dup2() 调用以处理 stdin 和 stderr , dup2() 从 pty 文件句柄中复制它们)。
如果用户可以在窗口中执行任意程序,您将需要小心 ncurses 程序——ncurses 将 move() 和 printw()/addch()/addstr() 命令转换为适当的控制台代码,所以盲目打印 ncurses 程序的输出会影响程序的输出并忽略窗口位置。GNU screen 是一个很好的例子来研究如何处理这个问题——它实现了一个 VT100 终端仿真器来捕获 ncurses 代码,并使用它自己的 termcap/terminfo 条目实现它自己的“屏幕”终端。Screen 的子程序在伪终端中运行。(xterm 和其他终端仿真器执行类似的技巧。)
最后说明:我还没有编译上面的代码。它可能有小错别字,但通常应该是正确的。如果您使用 mmap(),请确保使用 munmap()。此外,在完成 ls 输出后,您需要关闭(tmpfile)。unlink() 可能能够在代码中更早地进行,或者在 close() 调用之前——取决于你是否希望人们看到你玩的输出——我通常将 unlink() 直接放在 mkstemp 之后() 调用——如果 tmp 目录是磁盘支持的,这会阻止内核将文件写回磁盘(由于 tmpfs,这种情况越来越少见)。此外,在 unlink() 之后,您需要 free(tmpname) 以防止内存泄漏。strdup() 是必需的,因为 tmpname 由 mkstemp() 修改。
我能想到的一件事是使用 system() 执行命令,将其输出重定向到临时文件:
system("ls > temp");
然后打开文件 temp,读取其内容并将其显示在窗口上。
不是一个优雅的解决方案,但有效。
Norman Matloff 在他的《Unix 诅咒库简介》第 5 页中展示了一种方式:
// runs "ps ax" and stores the output in cmdoutlines
runpsax()
{ FILE* p; char ln[MAXCOL]; int row,tmp;
p = popen("ps ax","r"); // open Unix pipe (enables one program to read
// output of another as if it were a file)
for (row = 0; row < MAXROW; row++) {
tmp = fgets(ln,MAXCOL,p); // read one line from the pipe
if (tmp == NULL) break; // if end of pipe, break
// don’t want stored line to exceed width of screen, which the
// curses library provides to us in the variable COLS, so truncate
// to at most COLS characters
strncpy(cmdoutlines[row],ln,COLS);
// remove EOL character
cmdoutlines[row][MAXCOL-1] = 0;
}
ncmdlines = row;
close(p); // close pipe
}
...
然后他通过 ncurses 调用mvaddstr(...)
从数组中取出行。