如何使 ncurses 程序与其他 linux 实用程序一起工作?

how to make ncurses program working with other linux utils?

假设我有一个 ncurses 程序,它在 curses 屏幕上做一些工作,最后打印一些东西到 stdout。调用此程序 c.c,编译为 a.out.

我预计 cat $(./a.out) 首先启动 ncurses,经过一些操作后,a.out 退出并打印 c.cstdout,由 cat 读取, 从而打印文件 c.c.

的内容
#include <stdio.h>
#include <ncurses.h>

int main() {
    initscr();
    noecho();
    cbreak();
    printw("hello world");
    refresh();
    getch();
    endwin();
    fprintf(stdout, "c.c");
    return 0;
}

我也希望 ./a.out | xargs vimls | ./a.out | xargs less 能正常工作。

但是当我输入 ./a.out | xargs vim 时,hello world 永远不会出现。命令好像没有按顺序执行,vim打不开c.c.

使 ncurses 程序与其他 linux 实用程序一起工作的正确方法是什么?

ncurses 通过将一堆 ansi 转义符写入 stdout 来工作,终端将对其进行解释。您可以 运行 ./a.out > file 然后检查文件以查看您 实际上 正在写什么。程序混淆的原因将立即显而易见:

$ cat -vE file
^[(B^[)0^[[?1049h^[[1;24r^[[m^O^[[4l^[[H^[[Jhello world^[[24;1H^[[?1049l^M^[[?1l^[>c.c

正确的做法是当您检测到 stdout 不是终端时跳过所有 graphical/textual UI 部分,即它被程序而不是用户使用:

#include <unistd.h>
#include <stdio.h>
#include <ncurses.h>

int main() {
  if(isatty(1)) {
    // Output is a terminal. Show stuff to the user.
    initscr();
    noecho();
    cbreak();
    printw("hello world");
    refresh();
    getch();
    endwin();
  } else {
    // Output is consumed by a program.
    // Skip UI.
  }
  fprintf(stdout, "c.c");
  return 0;
}

这是典型的 Unix 行为。

如果您不想强制显示 UI,您可以

管道使用标准输出 (stdout) 和标准输入 (stdin)。

最简单的方法 - 而不是使用 initscr,它将输出初始化为使用 标准输出 ,使用 newterm,它允许您选择文件描述符,例如,

newterm(NULL, stderr, stdin);

而不是

initscr();

(几乎)相同
newterm(NULL, stdout, stdin);

顺便说一下,当你包含<ncurses.h>(或<curses.h>)时,就不需要包含<stdio.h>.

如果你想在管道的中间使用你的程序,那就更复杂了:你必须耗尽标准输入并打开实际的终端设备。但这是另一个问题(并且已经得到回答)。

延伸阅读: