如何在freopen(“out.txt”,“a”,stdout)之后将输出重定向回屏幕

#include  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系统调用?

我想不出一种以跨平台的方式做到这一点的方法,但是在GNU / Linux系统上(也可能是其他符合POSIX的系统)你可以freopen ("/dev/tty", "a", stdout) 。 这是你想要做的吗?

不幸的是,似乎没有一个好方法:

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

最好的建议是不要在这种情况下使用freopen。

一般来说,你不能。 你已经关闭了文件,这可能是管道或其他什么。 它不可重新开启。 您可能已保存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); 

另一个编辑:如果您使用它来重定向子进程的输出,就像其他人建议的注释一样,您可以在fork之后重定向它。

使用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  #include  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文件系统,则可以使用sprintf(buffer, "/dev/fd/%d", old_stdout) dup()创建从dup()返回的文件描述符隐含的文件名称。 sprintf(buffer, "/dev/fd/%d", old_stdout)然后使用具有该名称的freopen() 。 这将比此代码中使用的赋值更可靠。

更好的解决方案是使代码在任何地方使用’fprintf(fp,…)’,或者使用允许您设置自己的默认文件指针的覆盖函数:

mprintf.c

 #include "mprintf.h" #include  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  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 ./original_program | grep something (具有重定向输出)或从cron作业运行,然后打开/dev/tty通常不适合作为重新打开标准输出的方法,因为原始标准输出不是终端。

另请注意,如果在分叉和执行子程序之前使用标准输出的重定向并且在父级中恢复原始标准输出,则操作序列是错误的。 您应该fork并调整子进程的I / O(仅限),而不必修改父进程的I / 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重定向到开头,这可能不起作用。

以下代码(SwapIOB)用于要在其中存储stdout流以与预期结果文件进行比较的Testbenches中。

背景:使用_IOB结构管理文件流,该结构存储在20个_IOB条目的数组中。 这包括stdout流。 IOB存储在一个数组中。 创建文件时,应用程序代码会获取该数组中元素的ptr。 然后,应用程序代码将该ptr传递给OS以处理I / O调用。 因此,操作系统本身并不包含或依赖自己指向应用程序的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!"); 

发现这篇文章是因为我遇到了在一个流( stdout )中混合printfwprintf的输出的问题。 在研究了这个post之后,我发现只要不使用fclose() ,下面的代码就会很简单。 (在gcc上测试(Gentoo 4.8.3 p1.1,pie-0.5.9)4.8.3。)。 使用fclose导致以后对printf()的调用都失败,只有fprint继续正确地完成工作。

 int fDesc; /* file descriptor */ printf("byte-oriented output\n"); /* printf sets the output as byte-oriented */ fDesc = dup(fileno(stdout)); close(stdout); stdout = fdopen(fDesc,"w"); wprintf(L"wchar-oriented output\n"); /* wprintf sets it to wide-character compat. */ fDesc = dup(fileno(stdout)); close(stdout); stdout = fdopen(fDesc,"w"); /* reopen for byte-oriented output */ printf("byte-oriented output\n"); 

将输出重定向到shell中的其他文件或通过管道也可以正常工作。

 $> a.out > /tmp/out ; cat /tmp/out $> a.out | sed "s/oriented/aligned/" 

顺便说一句,freopen也可以正常工作。 在C ++代码中它将是:

 cout << "some cout text here"; stdout=freopen(NULL,"w",stdout); // NULL to reopen same file name! wcout << L" some wchar_t-s after them" stdout=freopen(NULL,"w",stdout); // reset the stdout again cout << " and some couts again." << endl;