0

このスタックオーバーフローの質問を読みましたが、stdout のリダイレクトに成功しました。しかし、上記のコードが機能する方法では、端末が完全に失われ、端末に何も印刷できなくなります。

私が達成したいのは、プログラムの出力 (私のコードと変更できないライブラリを使用する) を 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を使用しますが、ほとんどの場合、この動作を変更する方法はありません(ライブラリに埋め込まれた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);
        }

フォークが呼び出されたときに、子(pid == 0)の場合は、前に作成したパイプを取得し、独自のSTDOUTをこのパイプの書き込み端に切り替えます。

setvbuf(stdout, NULL, _IOLBF, 1000);

親がより簡単に取得できるように、バッファリングモードをlineに変更します。

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 に答える