- 締切済み
stdout/stderrを同一ファイルに出力
シェルのレベルでは単に: exec >> stdouterr.log 2>&1 とするだけで、2つのストリームを1つのファイルに出力できるようです。 ところがこれをC言語で実現することができません。例えば次のコード: FILE *fpout, *fperr; setbuf ( stdout, 0 ); setbuf ( stderr, 0 ); fpout = freopen( "outfile", "wb", stdout ), fperr = freopen( "outfile", "wb", stderr ); printf ( "処理を開始します。\n\n\n" ); fprintf(stderr, "stderrに出力します。\n" ); Msg = K_Link.LnkConvert (); printf ( "\n\n\n処理を完了しました。\n\n\n" ); fclose ( fpout ); fclose ( fperr ); を実行すると、下記のような『outfile』ができてしまいます。 処理を開始します。 MS-DOSコマンド"mkdir /public1/public/Pic/Compile_euc/K_LIB"を実行します。 MS-DOSコマンド"cp -r /public1/publ....../K_LIB/* /publ.../K_LIB"を実行します。 MS-DOSコマンド"chmod -R u+rw /public1/pu.._euc/K_LIB"を実行します。 ファstderrに出力します。 <-<-<-<-<-<-<-<-<-<-<-<-<-<-<- 何か変? Pic/Compile_euc/tmp.dat"をファイル"/public1/p..._LIB/Makefile"にコピー。 ファイル"/pub...p.dat"をファイル"/public....ile.gcc"にコピー。 | | | | | | | | | | の様に出力が壊されてしまいます。 2つのストリームからデータが出力されるタイミングがそれぞれ異なるようです。 どの様にしたらうまくいくのか助言ください。
- みんなの回答 (5)
- 専門家の回答
みんなの回答
- Tacosan
- ベストアンサー率23% (3656/15482)
#4 に書いたようにこの動作は「処理系定義」です. 言い換えると, 「確実に実行する」方法は標準 C には存在しません. 逆に言えば「環境によっては可能」なので, どのようなコンパイラを使いどのような OS で動かすのかがわかれば安心できる方法が見つかるかもしれません. あるいは, 「あなたが作るアプリから市販のCコンパイラを起動する」方法に依存するかもしれません. Unix 世界なら普通は dup2. Windows にも _dup2 という名前で存在してるらしい.
- Tacosan
- ベストアンサー率23% (3656/15482)
「混ぜるのは簡単だけど分離するのは難しい」んだから, そんな変なことはしない方が吉だと思う. 少なくともこの文章では「そんなことをなぜしなければならないのか」が読み取れないし. ちなみに「許されていません」とはなってません>#2. 「処理系定義」なので, 処理系によっては可能です... がその結果どうなるかは知らない.
お礼
皆様から早速ご連絡いただきながら、ご回答が遅くなり申し訳ありません。 取り急ぎ次のようにご連絡致します。 1.shsst14様 一般的に『禁じ手』であることは、ご指摘のとおりかもしれません。 しかしながら私の最初の質問にも記しましたとおり『シェル上のコマンドの組み合わせ ではできるではないか。』ということです。私自身は実際に試していませんが、web上 で『できる。』と答えている方は大勢いるようです。もしかしたらDOS窓上のDOSコマン ドを使ってもできるかもしれませんね。 シェルもCまたはC++プログラムであることを考えれば、『できない。』ことはないだろ うと思います(私自身はできなくて困っているのですが。)。 2.chie65535様 ご指摘の内容を私はよく理解できていません。 少し時間をかけてテストしてみます。 3.OrangeCup150様 わざわざテストコードまで添付いただきありがとうございます。 少し時間をかけて確認させていただきます。 4.Tacosan様 このようなことを質問するのは次の理由です。 私が作るアプリから市販のCコンパイラを起動します。その際にDOS窓上に表示される メッセージ全てをログファイルに納めたいのです。 私自身が作るプログラム上ならば、単に全てのメッセージをprintf()関数で書けば用は 足りるのですが、市販のCコンパイラはstdoutとstderrを区別して出力します。 つまりstdoutだけでログファイルを作ってしまうと、エラーが発生してもログを見ても 全然判らない、という間抜けなことになってしまいます。 そこで、表記のような質問をしたのでした。
- OrangeCup150
- ベストアンサー率62% (109/174)
・ストリームを追記モードで開く ・バッファリングをしない でやるとこちらの環境 (Windows SDK) ではうまくいきました。 体感はできませんが、常識的に考えてバッファリングをしないのでパフォーマンスはすごく悪くなっていると思います。 論理的には、ファイルストリームに対してバッファリング無しでアクセスされる、追記モードを使用するので処理の整合性は保障されると納得はできました。推測ですがバッファリングを使用すると非同期に処理が行われる(マルチスレッド化する)のでうまくいかないが、バッファリングを無効にするとシングルスレッドで処理が行われるのでうまくいくのでしょう。 以下、テストコードです。参考までに。 #include <stdio.h> #define QUOTE(str) #str #define NUM_QUOTE(num) QUOTE(num) #define FILE_LINE __FILE__ " の " NUM_QUOTE(__LINE__) " 行目" int main(int argc, char **argv) { FILE *o, *e, *info; int result; info = fopen("info", "w"); o = freopen("outfile", "a", stdout); if (o == NULL) { fprintf(info, "ERROR: " FILE_LINE "\n"); fclose(info); return 1; } else { fprintf(info, "OK: " FILE_LINE " freopen(stdout)\n"); } e = freopen("outfile", "a", stderr); if (e == NULL) { fprintf(info, "ERROR: " FILE_LINE "\n"); fclose(info); return 1; } else { fprintf(info, "OK: " FILE_LINE " freopen(stderr)\n"); } result = setvbuf(stdout, NULL, _IONBF, 0); if (result != 0) { fprintf(info, "ERROR: " FILE_LINE "\n"); fclose(info); return 1; } else { fprintf(info, "OK: " FILE_LINE " setvbuf(stdout)\n"); } result = setvbuf(stderr, NULL, _IONBF, 0); if (result != 0) { fprintf(info, "ERROR: " FILE_LINE "\n"); fclose(info); return 1; } else { fprintf(info, "OK: " FILE_LINE " setvbuf(stderr)\n"); } printf("処理を開始します\n"); fprintf(stderr, "stderr に出力します\n"); printf("処理を終了しました。"); result = fclose(o); if (result != 0) { fprintf(info, "ERROR: " FILE_LINE "\n"); fclose(info); return 1; } else { fprintf(info, "OK: " FILE_LINE " fclose(o)\n"); } result = fclose(e); if (result != 0) { fprintf(info, "ERROR: " FILE_LINE "\n"); fclose(info); return 1; } else { fprintf(info, "OK: " FILE_LINE " fclose(e)\n"); } fclose(info); return 0; }
- chie65536(@chie65535)
- ベストアンサー率44% (8754/19864)
>どの様にしたらうまくいくのか助言ください。 CのI/Oストリームでは、1つのファイルを同時に2回以上オープンするのは許されていません。 で、以下のような事は絶対にやっちゃ駄目ですが、 stdio.hを見てみると typedef struct { (中身は略) } FILE; /* This is the FILE object */ の付近に extern FILE _streams[]; とか #define stdin (&_streams[0]) #define stdout (&_streams[1]) #define stderr (&_streams[2]) とかって記述があります。 これは、stdoutはFILE型構造体の配列_streamsの1の要素のポインタ、同様にstdoutはFILE型構造体の配列_streamsの2の要素のポインタ、と言う事です。 なので #undef stderr #define stderr stdout とやってしまえば、stdoutもstrerrも「同じ内容のストリームオブジェクト」を意味します。 但し、ライブラリ内で&_streams[2]を参照している箇所は「そのまま変化しない」のでご注意を。 でも、こんな事は絶対にやっちゃいけません。 本当なら、1行書き出すたびに、fopenとfcloseを繰り返して「1つのファイルを同時に2回以上オープンしない」と言う状態を保たないといけません。
- shsst14
- ベストアンサー率40% (38/94)
同じファイルを別の名前で同時に開いてしまうのですか? 考えただけで問題が出そうです。 stdoutかstderrのどちらかひとつではだめなのでしょうか?
お礼
様々にご支援ありがとうございました。 いろいろじたばたとやってみたのですが、私には問題解決できず諦 めました。 最終的に、私が作成するソフトウエアから直接市販コンパイラを起 動するのではなく、私のソフトはコンパイラ起動のためのバッチ ファイルを作成.....バッチファイルを起動する際に次のDOSコマン ドを用います。 compile.bat > compile.log 2>&1 これで、バッチファイル実行中のstdout/stderrは奇麗に compile.logファイルに収まります。 何だか『DOSコマンドを駆使する。』ことには抵抗があるのですが 仕方がありませんね。