在 ncurses 中捕获命令行输出

发布于 2024-08-16 04:33:15 字数 110 浏览 2 评论 0原文

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

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

how to capture the command line output in a window using ncurses?

Suppose I am excecuting a command like "ls" I want to print that output in a specific window which is designed in ncurses. I am new to ncurses.help me.Thanks in advance

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

双马尾 2024-08-23 04:33:15

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

system("ls > temp");

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

这不是一个优雅的解决方案,但可行。

One thing I can think of is using system() to execute the command, redirecting its output to a temp file:

system("ls > temp");

Then opening the file temp, reading its content and displaying it on the window.

Not an elegant solution, but works.

叹梦 2024-08-23 04:33:15

更优雅的解决方案可能是在程序中实现重定向。查看 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 执行此操作另外,您应该能够在 fork() 之后设置 $LINES 和 $COLUMNS 环境变量,以使 ls 能够漂亮地打印到您的窗口大小 - 假设您使用 pty 的基本更改是删除 tmpfile = mkstemp(...); 行并将其及其周围逻辑替换为 pty 打开逻辑,并扩展 dup2() 调用以处理 stdin 和 stderr ,从 pty 文件句柄中 dup2() 处理它们)。

如果用户可以在窗口中执行任意程序,则需要小心ncurses程序——ncurses将move()和printw()/addch()/addstr()命令翻译成适当的控制台代码,因此盲目地打印 ncurses 程序的输出将破坏程序的输出并忽略您的窗口位置。 GNU screen 是研究如何处理这个问题的一个很好的例子——它实现了一个 VT100 终端仿真器来捕获 ncurses 代码,并使用自己的 termcap/terminfo 条目实现了自己的“screen”终端。 Screen 的子程序在伪终端中运行。 (xterm 和其他终端仿真器执行类似的技巧。)

最后注意:我还没有编译上面的代码。可能有一些小错别字,但大体上应该是正确的。如果使用 mmap(),请确保使用 munmap()。另外,完成 ls 输出后,您需要关闭(tmpfile)。 unlink() 可能可以在代码中更早地执行,或者在 close() 调用之前 - 取决于您是否希望人们看到您正在使用的输出 - 我通常将 unlink() 直接放在 mkstemp 之后() 调用——如果 tmp 目录是磁盘支持的,这会阻止内核将文件写回磁盘(由于 tmpfs,这种情况越来越少见)。另外,您需要在 unlink() 之后释放 free(tmpname) 以防止内存泄漏。 strdup() 是必需的,因为 tmpname 是由 mkstemp() 修改的。

The more elegant solution might be to implement the redirect within your program. Look into the dup() and dup2() system calls (see the dup(2) manpage). So, what you would want to do is (this is essentially what the shell called by system() ends up doing):

Code Snippet:

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  

For more picky programs (ones that care that they have a terminal for input and output), you'll want to look into pseudo ttys, see the pty(7) manpage. (Or google 'pty'.) This would be needed if you want ls to do its multicolumn pretty-printing (eg, ls will detect it is outputting to a file, and write one filename to a line. If you want ls to do the hard work for you, you'll need a pty. Also, you should be able to set the $LINES and $COLUMNS environment variables after the fork() to get ls to pretty print to your window size--again, assuming you are using a pty. The essential change is that you would delete the tmpfile = mkstemp(...); line and replace that and the surrounding logic with the pty opening logic and expand the dup2() call to handle stdin and stderr as well, dup2()ing them from the pty file handles).

If the user can execute arbitrary programs in the window, you'll want to be careful of ncurses programs--ncurses translates the move() and printw()/addch()/addstr() commands into the appropriate console codes, so blindly printing the output of ncurses programs will stomp your program's output and ignore your window location. GNU screen is a good example to look into for how to handle this--it implements a VT100 terminal emulator to catch the ncurses codes, and implements its own 'screen' terminal with its own termcap/terminfo entries. Screen's subprograms are run in pseudo-terminals. (xterm and other terminal emulators perform a similar trick.)

Final note: I haven't compiled the above code. It may have small typos, but should be generally correct. If you mmap(), make sure to munmap(). Also, after you are done with the ls output, you'll want to close(tmpfile). unlink() might be able to go much earlier in the code, or right before the close() call--depends on if you want people to see the output your playing with--I usually put the unlink() directly after the mkstemp() call--this prevents the kernel from writing the file back to disk if the tmp directory is disk backed (this is less and less common thanks to tmpfs). Also, you'll want to free(tmpname) after you unlink() to keep from leaking memory. The strdup() is necessary, as tmpname is modified by mkstemp().

屋顶上的小猫咪 2024-08-23 04:33:15

Norman Matloff 在他的Unix Curses 库简介中展示了第五页的一种方式:

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

Norman Matloff shows in his Introduction to the Unix Curses Library on page five a way:

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

... 

He then calls mvaddstr(...) to put out the lines from the array through ncurses.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文