web-dev-qa-db-ja.com

freopen( "out.txt"、 "a"、stdout)の後で出力を画面にリダイレクトする方法

#include <stdio.h>

int main() {
    printf("This goes to screen\n");
    freopen("out.txt", "a", stdout);
    printf("This goes to out.txt");
    freopen("/dev/stdout", "a", stdout);
    printf("This should go to screen too, but doesn't\n");

    return 0;
}

Freopenを呼び出してstdoutをout.txtにリダイレクトし、ファイルに何かを印刷します。今度はそれを画面にリダイレクトしますが、-freopen( "/ dev/stdout" 、 "a"、stdout);は機能しません。 ANSI CまたはPOSIXシステムコールを使用してそれを行う方法はありますか?

29
Hoffmann

これをクロスプラットフォームの方法で行う方法は考えられませんが、GNU/Linuxシステム(およびおそらく他のPOSIX準拠のシステム)でもfreopen ("/dev/tty", "a", stdout)を実行できます。これはあなたがやろうとしていたことですか?

22

残念ながら、良い方法はないようです:

http://c-faq.com/stdio/undofreopen.html

この状況では、freopenを使用しないことをお勧めします。

21
Andy West

fdopen()dup()、およびfreopen()を使用します。

_int old_stdout = dup(1);  // Preserve original file descriptor for stdout.

FILE *fp1 = freopen("out.txt", "w", stdout);  // Open new stdout

...write to stdout...   // Use new stdout

FILE *fp2 = fdopen(old_stdout, "w");   // Open old stdout as a stream

...Now, how to get stdout to refer to fp2?
...Under glibc, I believe you can use:

fclose(stdout);    // Equivalent to fclose(fp1);
stdout = fp2;      // Assign fp2 to stdout
// *stdout = *fp2;   // Works on Solaris and MacOS X, might work elsewhere.

close(old_stdout);   // Close the file descriptor so pipes work sanely
_

他の場所で確実に割り当てを行えるかどうかはわかりません。

実際に機能する疑わしいコード

以下のコードはSolaris 10とMacOS X 10.6.2で動作しましたが、信頼できるとは確信していません。構造体の割り当ては、Linux glibcで機能する場合と機能しない場合があります。

_#include <stdio.h>
#include <unistd.h>

int main(void)
{
    printf("This goes to screen\n");
    int old_stdout = dup(1);  // Consider dup(STDOUT_FILENO) or dup(fileno(stdout))
    FILE *fp1 = freopen("out.txt", "a", stdout);
    printf("This goes to out.txt\n");
    fclose(stdout);
    FILE *fp2 = fdopen(old_stdout, "w");
    *stdout = *fp2;                       // Unreliable!
    printf("This should go to screen too, but doesn't\n");

    return 0;
}
_

警告されなかったとは言えません— これは火で遊んでいます!

_/dev/fd_ファイルシステムのシステムを使用している場合は、dup()から返されたファイル記述子によって暗黙的に指定されたファイルの名前をsprintf(buffer, "/dev/fd/%d", old_stdout)で作成し、次に使用できます。その名前のfreopen()。これは、このコードで使用されている割り当てよりもはるかに信頼性が高くなります。

より優れたソリューションでは、コードでどこでも 'fprintf(fp、...)'を使用するか、独自のデフォルトのファイルポインターを設定できるカバー関数を使用します。

mprintf.c

_#include "mprintf.h"
#include <stdarg.h>

static FILE *default_fp = 0;

void set_default_stream(FILE *fp)
{
    default_fp = fp;
}

int mprintf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    if (default_fp == 0)
        default_fp = stdout;

    int rv = vfprintf(default_fp, fmt, args);

    va_end(args);
    return(rv);
 }
_

mprintf.h

_#ifndef MPRINTF_H_INCLUDED
#define MPRINTF_H_INCLUDED

#include <stdio.h>

extern void set_default_stream(FILE *fp);
extern int  mprintf(const char *fmt, ...);

#endif
_

明らかに、必要に応じてmvprintf()およびその他の関数を作成できます。

Mprintf()の使用例

次に、元のコードの代わりに、以下を使用できます。

_#include "mprintf.h"

int main()
{
    mprintf("This goes to screen\n");
    FILE *fp1 = fopen("out.txt", "w");
    set_default_stream(fp1);
    mprintf("This goes to out.txt\n");
    fclose(fp1);
    set_default_stream(stdout);
    mprintf("This should go to screen too, but doesn't\n");

    return 0;
}
_

(警告:テストされていないコード-信頼度が高すぎます。また、C99コンパイラを使用することを想定して記述されたすべてのコードは、主に関数の最初ではなく、最初に変数を宣言するためです。)


注意:

元のプログラムが_./original_program > file_または_./original_program | grep something_(リダイレクトされた出力を使用)として呼び出される場合、またはcronジョブから実行される場合、通常、_/dev/tty_を開くことは適切ではありません。元の標準出力が端末ではなかったため、標準出力を再び開く方法。

また、子プログラムのフォークと実行の前に標準出力のリダイレクトが使用され、元の標準出力が親で復元される場合、操作のシーケンスが誤っていることに注意してください。親のI/Oをまったく変更せずに、子のI/O(のみ)をフォークして調整する必要があります。

14

一般的に言えば、それはできません。あなたはファイルを閉じました、それはパイプか何かであったかもしれません。再開できません。 stdoutの値を保存してから、それにfopenを割り当ててから閉じ、古い値をコピーして戻した可能性があります。例:

FILE *o = stdout;
stdout=fopen("/tmp/crap.txt","a");
printf("Oh no!\n");
fclose(stdout);
stdout = o;

Mike Wellerは、以下のコメントでstdoutが常に書き込み可能であるとは限らないかもしれないと提案しました。この場合、そのようなものが役立つかもしれません:

int o = dup(fileno(stdout));
freopen("/tmp/crap.txt","a",stdout);
printf("Oh no!\n");
dup2(o,fileno(stdout));
close(o);

別の編集:他の場所でのコメントのように子プロセスからの出力をリダイレクトするためにそれを使用している場合は、フォークの後にリダイレクトできます。

Windowsでは、「CONOUT $」を開くことができます。

freopen("test.txt", "w", stdout);
printf("this goes to test.txt");
freopen("CONOUT$", "w", stdout);
printf("this goes to the console\n");

Stdoutが最初にリダイレクトされる場合、これはおそらく機能しません。

5
immibis

次のコード(SwapIOB)は、予想される結果ファイルと比較するためにstdoutストリームを格納するテストベンチで使用されます。

背景:ファイルストリームは、20個の_IOBエントリの配列に格納されている_IOB構造を使用して管理されます。これにはstdoutストリームが含まれます。 IOBは配列に格納されます。ファイルが作成されると、アプリケーションコードはその配列の要素へのptrを取得します。次に、アプリケーションコードはそのptrをOSに渡して、I/O呼び出しを処理します。したがって、OS自体は、アプリケーションのIOBへの独自のポインタを含んでいないか、依存していません。

要件:テストベンチを実行する場合、アプリケーションによって発行されるstdoutメッセージはファイルにリダイレクトする必要があります。ただし、テスト中のモジュールが完了した後、stdoutメッセージはコンソールにリダイレクトされます。

このルーチンはテスト済みで、現在Windows XP/Proシステムで使用されています。

void SwapIOB(FILE *A, FILE *B) {

    FILE temp;

    // make a copy of IOB A (usually this is "stdout")
    memcpy(&temp, A, sizeof(struct _iobuf));

    // copy IOB B to A's location, now any output
    // sent to A is redirected thru B's IOB.
    memcpy(A, B, sizeof(struct _iobuf));

    // copy A into B, the swap is complete
    memcpy(B, &temp, sizeof(struct _iobuf));

}  // end SwapIOB;

アプリケーションコードは、次のようなSwapIOB()を使用します。

FILE *fp;

fp = fopen("X", "w");

SwapIOB(stdout, fp);

printf("text to file X");

SwapIOB(stdout, fp);

fclose(fp);

printf("text to console works, again!");
1
JackCColeman