3

如何使用 ncurses 在窗口中捕获命令行输出?

假设我正在执行像“ls”这样的命令,我想在 ncurses 中设计的特定窗口中打印该输出。我是 ncurses 的新手。帮助我。在此先感谢

4

3 回答 3

1

更优雅的解决方案可能是在您的程序中实现重定向。查看 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 &gt= 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() 修改。

于 2010-01-03T07:31:46.963 回答
1

我能想到的一件事是使用 system() 执行命令,将其输出重定向到临时文件:

system("ls > temp");

然后打开文件 temp,读取其内容并将其显示在窗口上。

不是一个优雅的解决方案,但有效。

于 2009-12-24T09:40:24.287 回答
-1

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(...)从数组中取出行。

于 2014-11-16T01:16:23.710 回答