web-dev-qa-db-ja.com

Cからのstdinとstdoutの再ルーティング

stdinおよびstdout(および、おそらくstderr)ファイルハンドルを再度開きたいので、今後printf()またはputchar()またはputs()への呼び出しがファイルに移動します。 getc()への将来の呼び出しは、ファイルから行われます。

1)標準の入力/出力/エラーを永久に失いたくありません。プログラムの後半でそれらを再利用したい場合があります。

2)これらのファイルハンドルは、多くの場合またはグローバル(シャダー)に渡される必要があるため、新しいファイルハンドルを開きたくありません。

3)支援できない場合は、open()またはfork()またはその他のシステム依存の関数を使用したくありません。

だから基本的に、これを行うのはうまくいく:

stdin = fopen("newin", "r");

そして、もしそうなら、stdinの元の値をどのように戻すことができますか? FILE *に保存し、後で取得する必要がありますか?

60
Chris Lutz

freopen() を使用する理由C89仕様には、_<stdio.h>_に関するセクションの巻末注の1つに答えがあります。

116.freopen関数の主な用途は、標準のテキストストリーム(stderrstdin、またはstdout)。これらの識別子は変更可能である必要がないためfopen関数によって返される値が割り当てられる左辺値。

freopenは一般的に誤用されています。 stdin = freopen("newin", "r", stdin);。これはfclose(stdin); stdin = fopen("newin", "r");よりも移植性がありません。両方の式はstdinに割り当てを試みますが、これは割り当て可能とは限りません。

freopenを使用する正しい方法は、割り当てを省略することです:freopen("newin", "r", stdin);

78
bk1e

freopen() のようなものを探していると思います

15
John T

os関数 dup2() は、必要なものを提供する必要があります(必要なものを正確に参照しない場合)。

より具体的には、stdinファイル記述子を別のファイル記述子にdup2()し、stdinを使用して他の処理を行い、必要なときにコピーして戻すことができます。

Dup()関数は、開いているファイル記述子を複製します。具体的には、3番目の引数に0を指定したF_DUPFD定数コマンド値を使用して、fcntl()関数によって提供されるサービスへの代替インターフェイスを提供します。複製されたファイル記述子は、元のファイル記述子とロックを共有します。

成功すると、dup()は、元のファイルと共通の以下を含む新しいファイル記述子を返します。

  • 同じ開いているファイル(またはパイプ)
  • 同じファイルポインター(両方のファイル記述子が1つのファイルポインターを共有します)
  • 同じアクセスモード(読み取り、書き込み、または読み取り/書き込み)
9
gahooa
freopen("/my/newstdin", "r", stdin);
freopen("/my/newstdout", "w", stdout);
freopen("/my/newstderr", "w", stderr);

... do your stuff

freopen("/dev/stdin", "r", stdin);
...
...

これは、丸いペグ、四角い穴、Oメーターの針の頂点です。何を達成しようとしていますか?

編集:

Stdin、stdout、およびstderrは、新しく作成されたすべてのプロセスのファイル記述子0、1、および2であることに注意してください。 freopen()は同じfdを保持し、新しいストリームを割り当てるだけです。

したがって、これが実際にあなたがやりたいことをしていることを確実にする良い方法は:

printf("Stdout is descriptor %d\n", fileno(stdout));
freopen("/tmp/newstdout", "w", stdout);
printf("Stdout is now /tmp/newstdout and hopefully still fd %d\n",
   fileno(stdout));
freopen("/dev/stdout", "w", stdout);
printf("Now we put it back, hopefully its still fd %d\n",
   fileno(stdout));

ご覧のとおり、これはfreopen()の予想される動作だと思います。まだ3つのファイル記述子(および関連するストリーム)を使用しているだけです。

シェルがリダイレクトするものがないため、これはシェルリダイレクトをオーバーライドします。ただし、おそらくパイプが破損します。プログラムがパイプ(FIFO、パイプではなく)のブロッキング側にある場合は、SIGPIPEのハンドラーを必ず設定してください。

したがって、。/ your_program --stdout /tmp/stdout.txt --stderr /tmp/stderr.txtは、freopen()を使用して、実際の同じファイル記述子を保持することで簡単に実現できます。私が理解していないのは、なぜそれらを変更したら元に戻す必要があるのですか?確かに、誰かがどちらかのオプションを渡した場合、プログラムが終了するまでそれを持続させたいでしょうか?

9
Tim Post

それまでの間、これをすべて行うCソースコードライブラリがあり、stdoutまたはstderrをリダイレクトします。しかし、クールな部分は、インターセプトされたストリームに必要な数のコールバック関数を割り当てることができるため、単一のメッセージを複数の宛先、DB、テキストファイルなどに非常に簡単に送信できることです。

さらに、stdoutやstderrと同じ外観と動作の新しいストリームを作成するのは簡単です。これらの新しいストリームを複数の場所にリダイレクトすることもできます。

* oogleでU-Streams Cライブラリを探します。

3
john

freopenは簡単な部分を解決します。何も読んでおらず、dupdup2などのPOSIXシステムコールを使用する場合は、古いstdinを保持するのは難しくありません。あなたがそれから読み始めた場合、すべての賭けはオフです。

この問題が発生する状況を教えていただけますか?

古いstdinstdoutを放棄して、freopenを使用できる状況に固執することをお勧めします。

3
Norman Ramsey

これは最も便利で便利な方法です

freopen("dir","r",stdin);
0
afr0ck