通过freopen()重定向stdout后恢复

时间:2019-05-12 09:36:49

标签: c printf

这是How to capture output of printf?的后续活动-特别是jxh的答案。

我在回答Viesturs在2018年遇到的问题时遇到了与代码相同的麻烦。有人建议他打开一个新问题,他说他的帐户不允许这样做,所以我要开设一个同样的问题。

关于这个答案,最初的提问者在2012年说它有效。 Viesturs在2018年说,在代码运行之后,进一步的输出既没有出现在控制台上,也没有出现在重定向的文件中。

我遇到了同样的问题。使用以下代码,this shouldn't be redirected\n不会显示在控制台上,也不会出现在重定向的文件中。重定向的文件仅包含第一行。

我正在使用gcc 8.3.0,还尝试了gcc 6.5.0。 glibc 2.29。

请注意,我不想以后再使用freopen "CON"。即使可行,我仍要保留原始的stdout,因为它本身可以是重定向。 (尽管在我的测试中,它只是控制台。)

或者:jxh答案中有一个错误;我犯了同样的错误,维斯特斯是;或(我最好的猜测)早于gcc 6.5.0或glibc 2.29引入(或修复了)错误。

已重定向。c

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

void funcB() {
   printf("this should be redirected\n");
   fflush(stdout);
}

int main() {
   int stdout_fd = dup(STDOUT_FILENO);
   freopen("/tmp/redirected", "w", stdout);
   funcB();
   fclose(stdout);
   dup2(stdout_fd, STDOUT_FILENO);
   stdout = fdopen(STDOUT_FILENO, "w");
   close(stdout_fd);

   printf("this shouldn't be redirected\n");
   fflush(stdout); // shouldn't make a difference if this is here
}

输出:

$ gcc redirected.c
$ ./a.out
<THERE IS NO OUTPUT>
$ cat /tmp/redirected
this should be redirected

2 个答案:

答案 0 :(得分:3)

对此没有正确的解决方案可以涉及:

  • 分配给stdout,因为stdout不是变量/左值。它是一个宏,扩展为类型FILE *的表达式。如果它碰巧也是左值,那么这是发生它的任何特定实现的实现细节,而不是语言的一部分。
  • fclose(stdout),因为在fclose(stdout)之后,在程序的其余时间内,stdout在程序中任何位置的任何进一步使用(隐式(例如printf)或显式)执行,调用未定义的行为。

一个好的解决方案也不会涉及freopen,尽管您有能力能够做到这一点。相反,只需将dup2fflush一起使用。这个答案(由我本人)解释了如何做到:

https://stackoverflow.com/a/4832902/379897

用您要定向的文件代替"/dev/null"

答案 1 :(得分:2)

在通过stdin重新分配原始文件后,不能保证有任何方法可以将freopen()重新连接到原始文件。特别是,没有指定stdin宏是否扩展为可修改的左值,更不用说分配给结果的效果了。实际上,这首先就是freopen()的原因。

您尝试使用的方法是合理的,但又不能保证一定能奏效,并且我观察到您描述的相同行为。可能您的printf版本和我的版本在stdout的标识方面进行了优化,因此,给该符号分配新值不会影响该功能的输出目标,在打印之前要关闭该功能。

您正在使用POSIX接口(dupdup2),并且在符合POSIX的实现中,最可能的工作方法是避免关闭stdin,而是重新定义仅通过dup2()写入的文件。在这种情况下,您应该注意fflush(),这样的交换之前是这样的:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void funcB() {
   printf("this should be redirected\n");
   fflush(stdout);
}

int main() {
   int stdout_fd = dup(STDOUT_FILENO);
   int rd_fileno = open("/tmp/redirected", O_WRONLY | O_CREAT | O_TRUNC, 0600);
   dup2(rd_fileno, STDOUT_FILENO);
   funcB();
   fflush(stdout);
   dup2(stdout_fd, STDOUT_FILENO);
   close(stdout_fd);

   printf("this shouldn't be redirected\n");
   fflush(stdout); // shouldn't make a difference if this is here
}

该版本确实对我有用,它与POSIX中的重定向通常具有相同的基础。特别注意stdout永远不会关闭(程序终止时自动关闭)。