【问题标题】:capturing command line output in ncurses在 ncurses 中捕获命令行输出
【发布时间】:2009-12-24 09:28:02
【问题描述】:

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

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

【问题讨论】:

    标签: ncurses


    【解决方案1】:

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

    system("ls > temp");
    

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        Norman Matloff 在他的Introduction to the Unix Curses Library 第 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
        }
        
        ... 
        

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

        【讨论】:

        • 是的,该示例是 curses 示例应用程序的一部分,但显示的代码与 curses 无关。这只是常规的输出重定向
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-26
        • 2013-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-11
        相关资源
        最近更新 更多