在ncurses中捕获命令行输出

时间:2009-12-24 09:28:03

标签: ncurses

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

假设我正在执行像“ls”这样的命令我想在ncurses中设计的特定窗口中打印该输出。我是新来的ncurses.help me.Thanks提前

3 个答案:

答案 0 :(得分:1)

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

system("ls > temp");

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

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

答案 1 :(得分: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  

对于更挑剔的程序(那些关心它们有输入和输出终端的程序),你需要查看伪ttys,请参阅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屏幕是一个很好的例子来研究如何处理它 - 它实现了一个VT100终端模拟器来捕获ncurses代码,并使用自己的termcap / terminfo条目实现自己的'screen'终端。屏幕的子程序在伪终端中运行。 (xterm和其他终端仿真器执行类似的技巧。)

最后说明:我还没有编译上面的代码。它可能有小错别字,但一般应该是正确的。如果你是mmap(),请确保munmap()。此外,在完成ls输出后,您将要关闭(tmpfile)。 unlink()可能能够在代码中更早出现,或者在close()调用之前 - 取决于你是否希望人们看到你正在玩的输出 - 我通常在mkstemp之后直接放置unlink() ()调用 - 如果tmp目录是磁盘备份的话,这会阻止内核将文件写回磁盘(由于tmpfs,这种情况越来越少)。此外,在取消链接()之后,您将要释放(tmpname)以防止泄漏内存。 strdup()是必要的,因为tmpname由mkstemp()修改。

答案 2 :(得分:-1)

Norman Matloff在第五页的Introduction to the Unix Curses Library中显示了一种方式:

// 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
}

... 
然后他调用mvaddstr(...)通过ncurses从数组中输出行。