0

我已经阅读了这个 stackoverflow 问题,并且我成功地重定向了标准输出。但是上述代码的工作方式,我完全失去了我的终端,这意味着我不能在上面打印任何东西。

我想要实现的是使用 ncurses 格式化我的程序的输出(它将使用我的一些代码和一些我无法修改的库),并有一个窗口可以显示任何虚假的 printf。

有没有办法可以做到这一点?

    saved_stdout = dup(STDOUT_FILENO);  /* save stdout for display later $

    if( pipe(out_pipe) != 0 ) {          /* make a pipe */
        exit(1);
    }

    dup2(out_pipe[1], STDOUT_FILENO);   /* redirect stdout to the pipe */
    close(out_pipe[1]);

    initscr();                      /* Start curses mode            */

这样我就不能在屏幕上打印任何东西。

4

1 回答 1

0

为了达到我想要的效果,我必须解决一个问题:默认情况下,所有输出函数都使用 STDOUT,并且对于大多数输出​​函数,我无法改变这种行为(想想我没有源代码在 lib 中嵌入 printf ,或 ncurses 本身)。

因此,如果您修改 STDOUT,一切都会受到影响。

但我希望我的程序保留对 STDOUT 的控制。因此,您必须以某种方式将获得真正 STDOUT 的内容和获得STDOUT 的内容分开。分离听起来很像叉子......所以叉子就是这样。

本质上,您在一个子进程中分叉了所有程序的大部分,您为此修改了 STDOUT 管道它。父进程保持对 STDOUT 的控制,并且能够读取管道并对其中的数据执行任何操作。

#include <ncurses.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
#include <sstream>
#define MAX_LEN 99

WINDOW *create_newwin(int height, int width, int starty, int startx, int vert, int horz);

char buffer[MAX_LEN+1] = {0};
int out_pipe[2];
int saved_stdout;

volatile sig_atomic_t s_interrupted = 0;
volatile sig_atomic_t alarm_expired = 0;


static void s_signal_handler (int signal_value)
{
    if (signal_value == SIGTERM || signal_value == SIGSTOP || signal_value == SIGINT)
        s_interrupted = 1;
    if (signal_value == SIGALRM)
        //signal(SIGALRM,alarm_wakeup);
        alarm_expired = 1;
}

static void s_catch_signals (void)
{
    struct sigaction action;
    action.sa_handler = s_signal_handler;
    action.sa_flags = 0;
    sigemptyset (&action.sa_mask);
    sigaction (SIGINT, &action, NULL);
    sigaction (SIGTERM, &action, NULL);
    sigaction (SIGALRM, &action, NULL);
}

int main (int arg, char **argv) {

    const char mesg[]="Testing stdout redirection";
    WINDOW *my_win, *my_win2;
    int cycle = 0, pid, p[2], row, col;

if(pipe(p) == -1)
{
    perror("pipe call error");
    return(1);
}

    switch (pid=fork())
    {
        case -1: perror("error: fork call");
            return(2);

        case 0:  /* if child then write down pipe */
            if(dup2(p[1], 1) == -1 ) /* stdout == write end of the pipe */
            {
                perror( "dup2 failed" );
                return(1);
            }
            s_catch_signals ();
            setvbuf(stdout, NULL, _IOLBF, 1000);
            while(!s_interrupted)
            {
                printf("Test %d\n",cycle);
                cycle++;
                usleep(10000);
            }
            printf("\n\tChild quitting cleanly\n\n");
            break;
        default:
            s_catch_signals ();
            std::string mystr;
            if(dup2(p[0], 0 ) == -1 ) /* stdin == read end of the pipe */
            {
                perror( "dup2 failed" );
                return(1);
            }
            initscr();              /* start the curses mode */
            getmaxyx(stdscr,row,col);       /* get the number of rows and columns */
            mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
                            /* print the message at the center of the screen */

            refresh();
            my_win2 = create_newwin(10,col,row-10, 0, 0, 0);    
            wrefresh(my_win2);
            my_win = create_newwin(8,col-2,row-9, 1, ' ', ' ');
            wrefresh(my_win);
            wprintw(my_win,"This screen has %d rows and %d columns\n",row,col);
            wprintw(my_win,"Try resizing your window(if possible) and then run this program again\n");
            scrollok(my_win,TRUE);
            int myy,myx;
            std::stringstream ss;
            while(!s_interrupted)
            {
                while( std::getline(std::cin, mystr) )
                {
                    ss.clear();
                    ss << mystr << std::endl;
                    mystr = ss.str();
                    waddstr(my_win,mystr.c_str());
                    wrefresh(my_win);
                }
                usleep(100000);

            }
            endwin();


            break;
    }
    endwin();
    printf("\n\tProgram quitting cleanly\n\n");
    return (EXIT_SUCCESS);
}

WINDOW *create_newwin(int height, int width, int starty, int startx, int vert, int horz)
{   WINDOW *local_win;

    local_win = newwin(height, width, starty, startx);
    box(local_win, vert , horz);        /* 0, 0 gives default characters 
                     * for the vertical and horizontal
                     * lines            */
    wrefresh(local_win);        /* Show that box        */

    return local_win;
}

该程序准确地显示了这种行为:主程序产生一个子程序,它将自己的 STDOUT 重定向到管道。然后,该管道由父级读取,父级将其输出到受控窗口内。屏幕的其余部分可以由父母自由操作,但这是必要的。

switch (pid=fork())
{
    case 0:  /* if child then write down pipe */
        if(dup2(p[1], 1) == -1 ) /* stdout == write end of the pipe */
        {
            perror( "dup2 failed" );
            return(1);
        }

当调用 fork 时,如果我们是孩子 (pid==0) 则取我们之前创建的管道,并将我们自己的 STDOUT 切换到该管道的写入端。

setvbuf(stdout, NULL, _IOLBF, 1000);

将缓冲模式改为行,这样我们的父级可以更轻松地抓取它。

while(!s_interrupted)
{
    printf("Test %d\n",cycle);
    cycle++;
    usleep(10000);
}

生成一些简单的文本来检查它是否有效。

    switch (pid=fork())
    {
        default:
        if(dup2(p[0], 0 ) == -1 ) /* stdin == read end of the pipe */
        {
            perror( "dup2 failed" );
            return(1);
        }

如果我们是父级,请将我们的 STDIN 设置为我们之前创建的管道的读取端。

    initscr();              /* start the curses mode */
    getmaxyx(stdscr,row,col);       /* get the number of rows and columns */
    mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
                    /* print the message at the center of the screen */

    refresh();
    my_win2 = create_newwin(10,col,row-10, 0, 0, 0);    
    wrefresh(my_win2);
    my_win = create_newwin(8,col-2,row-9, 1, ' ', ' ');
    wrefresh(my_win);
    wprintw(my_win,"This screen has %d rows and %d columns\n",row,col);
    wprintw(my_win,"Try resizing your window(if possible) and then run this program again\n");
    scrollok(my_win,TRUE);

初始化 ncurses 并创建一些窗口。我们使用 my_win2 绘制边框,使用无边框的 my_win 实际包含数据。此外,我们将 my_win 设置为自动滚动,以便我们可以继续在其上打印,而不必担心空间不足。

        while( std::getline(std::cin, mystr) )
        {
            ss.clear();
            ss << mystr << std::endl;
            mystr = ss.str();
            waddstr(my_win,mystr.c_str());
            wrefresh(my_win);
        }
        usleep(100000);

在一个轻松节奏的循环中,我们检查我们的 STDIN,如果我们有新的行,我们只需将它们添加到我们的滚动窗口中。因此,当我们的进程被安排在活动状态时,我们会花时间处理大量输出,而不是重复轮询。

这被证明是对所提出问题的有效解决方案。

于 2013-02-06T17:13:43.797 回答