【问题标题】:Restoring stdout after redirecting it via freopen()通过 freopen() 重定向后恢复标准输出
【发布时间】:2019-05-12 09:36:49
【问题描述】:

这是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"。即使可行,我也想保留原始标准输出,因为它本身可能是重定向。 (虽然在我的测试中,它只是控制台。)

要么:jxh 答案中有一个错误;我犯了与 Viesturs 相同的错误;或者(我的最佳猜测)在 gcc 6.5.0 或 glibc 2.29 之前的某个时间引入(或修复)了一个错误。

redirected.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

【问题讨论】:

  • 我敦促您捕获函数调用的返回值,以检查它们是否存在错误代码。如果您关心它们是否会失败,则必须始终对可能失败的函数执行此操作。
  • stdout 不是您可以像这样分配的变量/左值。这是一个扩展为FILE * 类型表达式的宏。分配给它总是错误的。在 fclose 之后,您将永远无法在整个进程生命周期内再次使用 stdout。

标签: c printf


【解决方案1】:

没有正确的解决方案可能涉及:

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

一个好的解决方案也不涉及freopen,尽管您也许能够完成这项工作。相反,只需使用dup2fflush。这个答案(由我)解释了如何做到这一点:

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

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

【讨论】:

【解决方案2】:

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

您尝试使用的方法是合理的,但同样不能保证有效,我观察到您描述自己的相同行为。可能您的 printf 和我的版本已经在 stdout 的标识上进行了优化,因此为该符号分配新值不会影响该函数的输出目标,您在打印之前将关闭它。

您正在使用 POSIX 接口(@98​​7654327@ 和 dup2),并且在符合 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 永远不会关闭(程序终止时自动关闭除外)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    • 1970-01-01
    • 2014-10-11
    • 2011-06-01
    • 1970-01-01
    • 2011-12-01
    相关资源
    最近更新 更多