• 締切済み

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つのストリームからデータが出力されるタイミングがそれぞれ異なるようです。 どの様にしたらうまくいくのか助言ください。

  • trka
  • お礼率66% (12/18)

みんなの回答

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

#4 に書いたようにこの動作は「処理系定義」です. 言い換えると, 「確実に実行する」方法は標準 C には存在しません. 逆に言えば「環境によっては可能」なので, どのようなコンパイラを使いどのような OS で動かすのかがわかれば安心できる方法が見つかるかもしれません. あるいは, 「あなたが作るアプリから市販のCコンパイラを起動する」方法に依存するかもしれません. Unix 世界なら普通は dup2. Windows にも _dup2 という名前で存在してるらしい.

trka
質問者

お礼

様々にご支援ありがとうございました。 いろいろじたばたとやってみたのですが、私には問題解決できず諦 めました。 最終的に、私が作成するソフトウエアから直接市販コンパイラを起 動するのではなく、私のソフトはコンパイラ起動のためのバッチ ファイルを作成.....バッチファイルを起動する際に次のDOSコマン ドを用います。 compile.bat > compile.log 2>&1 これで、バッチファイル実行中のstdout/stderrは奇麗に compile.logファイルに収まります。 何だか『DOSコマンドを駆使する。』ことには抵抗があるのですが 仕方がありませんね。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

「混ぜるのは簡単だけど分離するのは難しい」んだから, そんな変なことはしない方が吉だと思う. 少なくともこの文章では「そんなことをなぜしなければならないのか」が読み取れないし. ちなみに「許されていません」とはなってません>#2. 「処理系定義」なので, 処理系によっては可能です... がその結果どうなるかは知らない.

trka
質問者

お礼

皆様から早速ご連絡いただきながら、ご回答が遅くなり申し訳ありません。 取り急ぎ次のようにご連絡致します。 1.shsst14様 一般的に『禁じ手』であることは、ご指摘のとおりかもしれません。 しかしながら私の最初の質問にも記しましたとおり『シェル上のコマンドの組み合わせ ではできるではないか。』ということです。私自身は実際に試していませんが、web上 で『できる。』と答えている方は大勢いるようです。もしかしたらDOS窓上のDOSコマン ドを使ってもできるかもしれませんね。 シェルもCまたはC++プログラムであることを考えれば、『できない。』ことはないだろ うと思います(私自身はできなくて困っているのですが。)。 2.chie65535様 ご指摘の内容を私はよく理解できていません。 少し時間をかけてテストしてみます。 3.OrangeCup150様 わざわざテストコードまで添付いただきありがとうございます。 少し時間をかけて確認させていただきます。 4.Tacosan様 このようなことを質問するのは次の理由です。 私が作るアプリから市販のCコンパイラを起動します。その際にDOS窓上に表示される メッセージ全てをログファイルに納めたいのです。 私自身が作るプログラム上ならば、単に全てのメッセージをprintf()関数で書けば用は 足りるのですが、市販のCコンパイラはstdoutとstderrを区別して出力します。 つまりstdoutだけでログファイルを作ってしまうと、エラーが発生してもログを見ても 全然判らない、という間抜けなことになってしまいます。 そこで、表記のような質問をしたのでした。

回答No.3

・ストリームを追記モードで開く ・バッファリングをしない でやるとこちらの環境 (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; }

  • chie65535
  • ベストアンサー率43% (8512/19350)
回答No.2

>どの様にしたらうまくいくのか助言ください。 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)
回答No.1

同じファイルを別の名前で同時に開いてしまうのですか? 考えただけで問題が出そうです。 stdoutかstderrのどちらかひとつではだめなのでしょうか?

関連するQ&A

  • 標準出力と標準エラー出力を時系列にファイルへ

    例として、perlなどで、(test.plとします)  print "stdout1\n";  print STDERR "STDERR1\n";  print "stdout2\n";  print STDERR "STDERR2\n";  print "stdout3\n";  print STDERR "STDERR3\n"; このように、標準出力と、標準エラー出力が混在した状態の処理があった場合、 コマンドプロンプト(Windows2000)にて、 C:\>test.pl とすると、 stdout1 STDERR1 stdout2 STDERR2 stdout3 STDERR3 のように時系列に出力されますが、これをログファイルに取ろうとして、 C:\>test.pl 1>log.txt 2>&1 とすると、 C:\>cat log.txt STDERR1 STDERR2 STDERR3 stdout1 stdout2 stdout3 のように、標準エラー出力が先に吐き出されてしまいます。 これを画面出力時と同様に時系列で取れるようにしたいのですが、どのようにすれば良いでしょうか? 単純なことで困っています。よろしくお願いします。

  • 標準出力/標準エラー出力を時系列にファイルへ

    画面への文字出力が、標準出力(stdout)と標準エラー出力(stderr)の 両方もつような、コンソールアプリ(exe形式)のツールを動作させたときに、 画面上には、 stdout1 STDERR1 stdout2 STDERR2 stdout3 STDERR3 のような順番で処理順にメッセージが出るのですが、 これをファイルに落とそうとして、  C:\>hoge.exe 1> log.txt 2>1& とすると、log.txtの中身が、 C:\>type log.txt STDERR1 STDERR2 STDERR3 stdout1 stdout2 stdout3 のような標準エラー出力が先に吐き出される順番になってしまっています。 これを、exe実行前に、MS-DOSとして何らかの設定を行うことで、 ファイルに落とした時も、出力された文字が時系列に保存されるように する方法はあるでしょうか? よろしくお願いします。

  • stderrとstdout

    次のようなプログラムを、./a.outと./a.out>out.txtのように実行したところ、前者は output1 後者は output1 output2 output3 のようになりましたが、なぜこうなるか理由がわかりません。 自分では、stderrとstdoutが関係してるのだと思うのですが、初心者なのでこれらがどういうプログラムなのかもよくわかっていません。どなかなぜこうなるのか教えていただけないでしょうか。 #include<stdio.h> int main(int argc,char **argv){ fprintf(stderr,"output1\n"); fprintf(stdout,"output2\n"); printf("output3\n") }

  • 標準出力と標準エラー出力を変数にセットしたいです。

    始めたばかりの初心者の為、変な質問でしたら申し訳ありません。 標準出力と標準出力を別々の変数にセットしたいのですが、そのやり方が分からず困っています。 やりたいことは、 コマンド(diffやcatなどの)実行結果の標準出力と標準エラー出力を それぞれ「任意の文字_受取パラメータの値」にセットすることをしたいです。 下記は、1回ファイルに出力して、それを読んで変数にセットするように記述したものです。 ※記述間違っていたらすみません。 ファイルに書かなくても、パイプやその他コマンドなどで出来る方法はないでしょうか? ex) test.sh 1.txt 2.txt TEST01 で実行 #!/bin/sh parm1 = $1 parm2 = $2 parm3 = $3 diff "${parm1}" "${parm2}" > test.log 2> err.log eval w_stdout_${parm3}=¥`cat test.log¥` eval w_stderr_${parm3}=¥`cat err.log¥` eval echo "stdout:¥"¥{w_stdout_${parm3}}¥"" eval echo "stderr:¥"¥{w_stderr_${parm3}}¥"" ※実行した結果 stdout:diff結果 stderr:空白 お手数ですが、何卒宜しくお願い致します。

  • PHP5の外部コマンド実行で、バッチファイルのエラーレベル値と標準出力(標準エラー?)の3つを取得できる関数はありますか?

    PHP5のWindows版を使っています。 外部コマンドを実行して、Windowsのバッチファイルのエラーレベル値と標準出力(標準エラー?)の3つを取得できる関数はありますか? test.cmdの中味 echo 標準出力内容です。 echo バッチファイルで標準エラー出力はわかりませんが exit /b 2 list ($ret, $stdout, $stderr) = 何か関数("test.cmd") echo $ret; // 2 echo $stdout; // 標準出力内容です。\nバッチファイルで標準エラー出力はわかりませんが echo $stderr; // ???? よろしくお願い致します。

    • ベストアンサー
    • PHP
  • 【ruby】zipファイルデータをSTDOUTに出力したい(file descriptorをSTDOUTに切り替えるには)

    質問を見ていただいて有難うございます。 長文になりますが、宜しくお願いいたします。 最終的な質問をまとめると、 『file descriptorをSTDOUTに切り替えるにはどうしたら良いですか?』 になるかと思いますが、目的から経緯についても以下に記します。 zipファイルを標準出力に書き出したいです。 プログラム上で作り出したzipファイル形式のデータを、 ディスクに書き出すのではなく、標準出力に書き出したいのです。 zipファイルをレスポンスするcgiを作りたいからです。 zipファイルを生成するライブラリは、探したところ ・rubyzip ・zipruby というのがありました。 ziprubyには、Zip::Archive.open_bufferというメソッドがあったので、 これを利用して以下の様に書いてみました。(行頭スペースは全角です) ------------------------------------------------------------- #!/usr/local/bin/ruby require 'cgi' require 'rubygems' require 'zipruby' body='' Zip::Archive.open_buffer(body, Zip::CREATE) do |ar|  ar.add_buffer('first_entry.txt','Hello world!')  ar.add_dir('adir')  ar.add_buffer('adir/first_entry.txt','Hello again!') end CGI.new().out({  'status'=>'OK',  'type'=>'application/zip',  'Content-Disposition'=>'attachment; filename="hoge.zip"' }){body} ------------------------------------------------------------- とりあえずは、これで目的を達成できたのですが、 巨大なzipファイル生成すると、クライアント(ブラウザ)がタイムアウトしてしまいます。 そこで、CGI.new().outを使うのをやめて、STDOUTに直接、”ストリーム”に書き出そうと思います。(Chunkで) rubyzipには、Zip::ZipOutputStream::open(ファイル名)というのがあります。 これをファイルではなく、STDOUTへ出力するように改造を試みました。 zip.rbというソースファイルを見ると、 @outputStream = File.new(fileName, "wb") という部分があるので、この@outputStreamをSTDOUTに変えてしまえばよいと考え、 以下の様に描いてみました。 (標準出力に書き出す部分のみです。) ------------------------------------------------------------- require 'zip/zip' module ZipKai  include Zip  class ZipOutputStreamKai < ZipOutputStream   def initialize(fileName)    super(fileName)    @fileName = fileName    #@outputStream = File.new(fileName, "wb")    @outputStream = IO.new(1, "wb")   end  end end ZipKai::ZipOutputStreamKai::open("my.zip") { |io|  io.put_next_entry("first_entry.txt")  io.write "Hello world!"  io.put_next_entry("adir/first_entry.txt")  io.write "Hello again!" } ------------------------------------------------------------- 実行してみると、以下エラーがでました。 C:/ruby/lib/ruby/gems/1.8/gems/rubyzip-0.9.1/lib/zip/zip.rb:1008:in `tell': Bad file descriptor (Errno::EBADF) ここで、質問です。 file descriptor(@outputStream)をSTDOUTに切り替えるにはどうしたら良いでしょうか? ※そもそも、他にもっと良い方法があるよといったご回答でもOKです。 ご指導のほど、宜しくお願いいたします。

  • コンソール出力をテキストに出力する方法(コンソール出力は残しつつ)

    【質問】  Perl内部で実行される実行ファイル(.exe)のコンソール出力(STDOUT,STDERR)をテキストファイルに出力したい。(Perlのコンソール出力も含む。) ※コンソール出力は残しつつ。 (コマンドプロンプトの)パイプとリダイレクトを使用して上手くいくかと思ったのですが、標準出力と標準エラーとでは標準エラーが先に出力されてしまい、本来出力される順番で出力されない。(標準出力はバッファにたまるため??) 何かいい方法はありませんでしょうか? 【環境】  Windows2000  Active Perl 5.8.8

    • ベストアンサー
    • Perl
  • Rubyのpopen3について

    はじめまして Rubyをはじめて1か月の初心者です。 popen3を使用するにあたって大変困っております。 popen3で外部コマンドを実行して、実行結果の標準出力をファイルとして格納しようと思っています。 外部コマンドの内容は、SSH接続でサーバへログインし、そこにあるシェルスクリプトを実行させるもので、 呼び出すシェルスクリプトに対して、Rubyから標準入力を渡し、シェルスクリプトが標準入力を受け取り、 それをもとに呼び出したシェルスクリプトがさらに別のシェルスクリプトを呼び出し、実行結果を標準出力としてRubyで受け取ってファイルに格納したいと思っています。 ソースとしては、以下のようにしてますが、標準入力を引き渡す方法が分かりません。 その為、標準入力を引き渡す部分は記述していません。 Open3.popen3('ssh localhost sh test.sh ') do |stdin, stdout, stderr|   begin    loop do     IO.select([stdout,stderr]).flatten.compact.each { |io|      if stdout != "" then       stdout_lines = stdout        puts("stdout_lines = " + stdout_lines)      end      stderr_lines = stderr if stderr != ""      puts("stderr_lines = " + stderr_lines)     }     break if $stdout.classed? && $stderr.closed?   end   rescue EOFError   end end JRubyの1.6.8なので、Rubyのバージョンは1.8.7相当です。 なにぶん初心者の為、なにをどうすれば良いか分からない状態です。 まず、実現が可能かを知りたいです。 実現可能であれば、標準入力を引き渡す方法を教えていただきたく。 よろしくお願いします。

  • 標準出力上でのファイルポインタの扱いで困っています。

    標準出力上でのファイルポインタの扱いで困っています。 簡単な例で説明すると、 下のプログラムはファイル内で、一秒ごとに左から順に1を0で置き換えていくもので正常に動作します。 open(F, '+< temp'); print F "11111111111111111111111111111111\n"; for(0..31){ seek(F, $_, 0); print F '0'; sleep(1); } close(F); 同様のことが標準出力でも可能かと思って以下のようにしてみたのですが、うまくいきません。 open(STDOUT, '+<'); print STDOUT "11111111111111111111111111111111\n"; for (0..31){ seek(STDOUT, $_, 0); print STDOUT '0'; sleep(1); } close(STDOUT); } 2つ目の例では動作を見るかぎり、ファイルポインタは1111.....の先頭からのオフセットではなく、その次の行(空行)の先頭からのオフセットとなっているように見えます。つまり古い行には戻れていないようです。 Seek()は標準出力上では使えないのでしょうか?

    • ベストアンサー
    • Perl
  • Rubyから外部プログラムを実行し、その出力を..

    Rubyから外部プログラムを実行し、その出力を受け取り、加工したいのですがよくわかりません。 stdin, stdout, stderr = win32-open3.popen3("XXX.exe") とすれば何とかいけそうなんですが、win32-open3が今は使われていないようです。 他に代わるものがあるのでしょうか? ちなみに、Rubyのバージョンは1.8.7です。 よろしくお願いします。

    • ベストアンサー
    • Ruby

専門家に質問してみよう