如何将使用 curses/ncurses 的 c 程序的输出传递给另一个程序?

How do I pass the output of a c program which use curses/ncurses to another program?

我有一个简单的 c 程序,它使用 ncurses 在屏幕上显示一些文本。当运行时,会打印出hello,当用户按向上箭头键时,会退出程序在屏幕上打印bye see you soon

我希望能够使用程序打印的输出,在这种情况下 bye see you soon 作为另一个程序的输入,例如我想将它传递给 grepcat,喜欢:

./main.out | grep "bye"

如果我可以像 echo "bye see you soon" | grep "bye"

或者使用命令替换之类的东西 $(...).

使用我当前的解决方案,如果我使用 ./main.out | grep "bye",我总是得到一个空白屏幕,我无法通过管道连接到 grep。

目前正在寻找 Linux/macOS 的解决方案。

我想知道:

我正在学习一些 c 语言,如果您能在回答中给我更多的上下文,我将不胜感激。

运行 程序:

make all
./main.out
================================== main.c

#include <stdio.h>
#include <curses.h>
#include <stdlib.h>

void print_menu(WINDOW *menu_win);

void bye(void)
{
    printf("bye see you soon\n");
}

void print_menu(WINDOW *menu_win)
{
    printf("hi");
    wrefresh(menu_win);
}

int main()
{
    WINDOW *menu_win;
    int c;

    initscr();
    clear();
    noecho();
    cbreak();

    menu_win = newwin(30, 30, 0, 0);
    keypad(menu_win, TRUE);
    refresh();
    print_menu(menu_win);
    
    while (1)
    {
        c = wgetch(menu_win);
        switch (c)
        {
        case KEY_UP:
            endwin();

            int i = atexit(bye);
            if (i != 0)
            {
                fprintf(stderr, "cannot set exit function\n");
                exit(EXIT_FAILURE);
            }
            exit(EXIT_SUCCESS);
            break;
        default:
            refresh();
            break;
        }
    }
    clrtoeol();
    refresh();
    endwin();
    return 0;
}
================================== Makefile

SHELL   = /bin/sh
PROGRAM = main
CC      = gcc
CFLAGS  = -g -O0 -Wall -Werror
LIBS    = -lncurses

all:
    $(CC) ./${PROGRAM}.c -o ./${PROGRAM}.out $(CFLAGS) $(LIBS)

传统上,使用 ncurses(或较旧的 curses)的程序不会向 stdout 输出任何内容——它们只使用 stdin(实际上 write 到 stdin 以写入终点站)。因此,他们不会“很好地”处理接受一个进程的标准输出并将其连接到另一个进程的标准输入的管道。

如果您想要一个程序与带有 ncurses 的 stdin 上的终端交互并向 stdout 输出一些东西,您可以这样做——stdin 和 stdout 不需要彼此有任何关系——这将允许您将 stdout 的输出通过管道传输到其他地方。

initscr 相当于 newterm(NULL, stdout, stdin),这很好地使得不可能将输出也通过管道传输到其他实用程序。如果你想两者都做,你可以通过将 initscr() 替换为类似以下内容来强制 ncurses 使用 /dev/tty 作为输入和输出:

    FILE* tty = fopen("/dev/tty", "r+");
    SCREEN* screen = newterm(NULL, tty, tty);
    set_term(screen);

如果这样做,您将希望避免在 ncurses 处于活动状态时写入 stdout(如果它未被重定向),因为 ncurses 假定 no-one else 正在写入控制台。

根据 stdout 是否已重定向,更改应用程序的行为可能会有用。您可以使用 isatty() 进行相当保守的测试。

我不太明白为什么你认为有必要在你的示例程序中使用atexit()。只要调用 endwin(),就可以安全地写入 stdout。 (但是print_menuHiprintf是有问题的。)

也可以使用 stderr 作为 ncurses 输出,假设它没有被重定向。我更喜欢使用 /dev/tty 因为它不会干扰使用 stderr 进行错误记录(使它没有被重定向的假设无效)。但如果这对你不重要,newterm(NULL, stderr, stdin) 可能更简单。

在任何情况下,请确保始终检查 return 值的错误指示(我在上面的代码片段中省略了)。