1

私は Ubuntu Linux 12.04 を使用しており、ncurses を使用するプログラムを作成しています。私のプログラムには、下位プロセスを実行するオプションがあります (「シェル エスケープ」)。私が行う従属プロセスを作成する前に

reset_shell_mode( );
putp( exit_ca_mode );  // From <term.h>

次に、下位プロセスが終了すると、curses の表示を次のように復元します。

putp( enter_ca_mode );  // From <term.h>
reset_prog_mode( );
refresh( );

これはうまくいきます。ただし、私のプログラムは、下位プロセスを起動する直前にいくつかの情報も出力したいと考えています。また、下位プロセスが終了するとき、完全な curses 表示に戻る前に、いくつかの追加情報を出力したいと考えています。したがって、私は(省略):

reset_shell_mode( );
putp( exit_ca_mode );
printf( "Don't forget... blah, blah\n" );
system( external_command );
printf( "Updating, etc\n" );
putp( enter_ca_mode );
reset_prog_mode( );
refresh( );

問題は、system() の呼び出しの直前と直後にプログラムによって生成されたテキストが表示されないことです。おそらく、curses 関連のバッファーにまだ入っていると思います。知らない。

親プロセスを子プロセスだけでなく端末にも出力するにはどうすればよいですか?

4

2 に答える 2

1

Curses は、画面がどのように見えるべきかを考えた独自のバッファーを保持しています。refresh() を呼び出すと、そのバッファーに一致するように画面が調整されます。つまり、curses が認識していないものはすべて上書きされます (*)。

printf、および外部コマンドの出力は、そのバッファーをバイパスして、画面に直接送信されます (より正確には、シェルから標準出力を継承するため、たまたま画面に接続されている標準出力に送信されます)。

したがって、printf の出力を curses に入れるには、printf を printw に置き換える必要があります。他のプログラムの出力を curses に取得するには、その出力を自分のプログラムにキャプチャしてから、curses にフィードする必要があります。

これを行う簡単な方法は、出力をファイルにリダイレクトしてから、ファイルを読み取ることです。

system("ls > tempfile");
if ((fp=fopen("tempfile", "r"))!=NULL) {
    while (fgets(buf, sizeof buf, fp))
        printw("%s", buf);
    fclose(fp);
}

警告: この例は、アイデアを提供するために大幅に簡略化されています。エラーをうまくキャッチできず、あらゆる種類のバッファ オーバーフローが発生しやすい fgets を使用し、一時ファイルに定数名を使用するため、多くの並行性の問題が発生します。

より良い方法は、プロセスと実行しようとしているプログラムの間にパイプを作成することです。

int p[2];
pipe(p);
if (fork()==0) {  // child process
    close(1);
    dup(p[1]);
    close(p[1]);
    close(p[0]);
    execlp("ls", "ls", NULL);
} else {          // parent process
    close(p[1]);
    if ((fp=fdopen(p[0], "r"))!=NULL) {
        while (fgets(buf, sizeof buf, fp))
            printw("%s", buf);
        fclose(fp);
    }
}

繰り返しますが、この例はかなり省略されています (そして、ブラウザーに直接入力し、コンパイルも実行もしていません)。それを本当に理解し、不足しているすべてのエラーチェックを追加するには、linux/unix プロセスモデル、パイプ、ファイル記述子と C ファイルポインターについて学びます。そこには多くのチュートリアルがあり、これは元の質問をはるかに超えています。

しかし、要約すると、curses で画面に何かを表示したい場合は、適切な curses 関数を使用する必要があります。curses をバイパスするものはすべて、curses が画面を更新するとすぐに上書きされる可能性があります。

(*) curses が画面と内部バッファの違いだけだと判断した場合、画面全体ではなく、異なる文字のみを更新します。そのため、curses が更新する必要がないと判断した画面の部分に外部プログラムが書き込みを行った場合、それらの部分はそのまま残ります。つまり、プログラムの出力の一部が残ります。

于 2013-12-05T12:39:45.157 に答える
0

例では

reset_shell_mode( );
putp( exit_ca_mode );
printf( "Don't forget... blah, blah\n" );
system( external_command );
printf( "Updating, etc\n" );
putp( enter_ca_mode );
reset_prog_mode( );
refresh( );

コールは端末設定のreset_shell_mode()復元を試みます。1 つの問題があります。curses (一般的に言えば、ncurses だけでなく) は、ターミナル モードを "raw" に設定します (I/O バッファリングによる干渉なしに入力文字を読み取れるようにするため) が、(パフォーマンスのために) 出力バッファリングも設定します。

これはsetvbuf、標準に従って確実にオフ/オンにすることができないのいくつかのバリアントでこれを行います。

このsetvbuf()関数は、stream が指すストリームが開いているファイルに関連付けられた後で、他の操作 (への呼び出しが失敗した場合を除く) がストリームに対して実行される前にsetvbuf()使用できます。

それは細かいことだけではありません。一部の実装では、バッファリングを破棄しようとするとコア ダンプが発生します。したがって、ncurses はそのままです。しかし、ここでも注意すべき点があります。

  • 2012 年後半まで(シグナルの問題を修正するため)、ncurses は標準出力 (またはその初期化に供給されたストリーム) と同じバッファーを出力に使用していました。
  • それ以来、ncurses はのバッファを使用します。putp同じ出力バッファリングをprintw使用するが、ncurses などの再描画操作中にフラッシュする別のバッファを使用するなどの特殊なケースがありますrefresh

いずれの場合も、この例の修正はfflush、前の呼び出しとは異なる出力ストリームを使用するときに使用することです。これはうまくいくはずです:

reset_shell_mode( );
putp( exit_ca_mode );
printf( "Don't forget... blah, blah\n" );
fflush(stdout); // added
system( external_command );
printf( "Updating, etc\n" );
putp( enter_ca_mode );
fflush(stdout); // added
reset_prog_mode( );
refresh( );
于 2016-05-14T14:44:50.060 に答える