如何在C中关闭stdout和stderr?

我需要为我的一个C程序关闭stdout和stderr。 如果不退出程序执行,怎么可能?

你尝试过什么? 不工作吗?

你可以:

 fclose(stdout); fclose(stderr); 

对于任何想知道为什么要这样做的人来说,这对于Unix上的守护进程/服务进程来说是一个相当普遍的任务。

但是,您应该知道关闭文件描述符可能会产生意想不到的后果:

  • 当您打开新文件时,将使用这些现在可用的描述符。 因此,例如,如果您随后fopen该文件描述符(至少在Linux上)将替换fd 1,即stdout。 随后使用此代码的任何代码都将写入此文件,这可能与您的意图不同。
  • 请参阅R ..对文件描述符与C库FILE*指针的注释。 特别:
    • 如果你在Linux下写一个封闭的fd,你会收到一个错误,但是:
    • 如果你使用一个使用stdoutstderr的C库函数(它们是FILE*指针(参见它们的定义 ),那么在FILE*关闭时写入这些函数是未定义的行为。这可能会以意想不到的方式使程序崩溃,而不是总是在该错误的一点。看到未定义的行为 。
  • 您的代码不是唯一受影响的部分。 您使用的任何库以及您启动的inheritance这些文件描述符作为其标准描述符的任何进程也会受到影响。

快速的单行解决方案是freopen()在Linux / OSX下使用/dev/null/dev/console或在Windows上使用nul 。 或者,您可以根据需要使用特定于平台的实现重新打开文件描述符/句柄。

如果要阻止应用程序写入控制台,则:

 #include  int main() { fprintf(stdout, "stdout: msg1\n"); fprintf(stderr, "stderr: msg1\n"); fclose(stdout); fprintf(stdout, "stdout: msg2\n"); // Please read the note setion below fprintf(stderr, "stderr: msg2\n"); fclose(stderr); fprintf(stdout, "stdout: msg3\n"); fprintf(stderr, "stderr: msg3\n"); } 

输出:

 stdout: msg1 stderr: msg1 stderr: msg2 

注意:文件关闭后任何尝试使用FILE指针都是错误的。 我在这种情况下这样做只是为了说明关闭这些文件描述符可能对您的应用程序做什么。

警告:我根本没有C经验,但是最近读了一张幻灯片,由RedHat员工和GNUlib维护人员Jim Meyering直接回答了这个问题: https ://www.gnu.org/ghm/2011/paris/slides/ jim-meyering-goodbye-world.pdf 。 我只是总结一下。

TL; DR

将closeout.c及其依赖项从GNUlib获取到源代码并调用

 atexit(close_stdout); 

作为你的主要第一行。

摘要

首先,有人提醒警告,引用POSIX :

因为在调用fclose()之后对流的任何使用导致了未定义的行为,所以除了在进程终止之前,不应该在stdin,stdout或stderr上使用fclose()……如果有任何atexit()由应用程序注册的处理程序,在最后一个处理程序完成之前不应该调用fclose()。 一旦fclose()用于关闭stdin,stdout或stderr,就没有标准方法可以重新打开任何这些流。

在文件描述符STDIN_FILENO,STDOUT_FILENO或STDERR_FILENO上使用close()后应立即执行重新打开这些文件描述符的操作。 ……此外,close()后跟重新打开操作(例如open(),dup()等)不是primefaces的; dup2()应该用于更改标准文件描述符。

关闭流而不处理其错误并不健壮,对于stdout和stderr也是如此。 以下是您需要处理的错误列表:

  • fclose(stdout)
  • ferror(stdout)又名上一个错误
  • __fpending(stdout)又名没有刷新的东西

正如GNUlib在close-stream.c中实现的那样 ,处理这些错误将在下面引用。

 int close_stream (FILE *stream) { const bool some_pending = (__fpending (stream) != 0); const bool prev_fail = (ferror (stream) != 0); const bool fclose_fail = (fclose (stream) != 0); /* Return an error indication if there was a previous failure or if fclose failed, with one exception: ignore an fclose failure if there was no previous error, no data remains to be flushed, and fclose failed with EBADF. That can happen when a program like cp is invoked like this 'cp ab >&-' (ie, with standard output closed) and doesn't generate any output (hence no previous error and nothing to be flushed). */ if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) { if (! fclose_fail) errno = 0; return EOF; } return 0; } 

注意: __fpending对glibc __fpending是特殊的,可能不是可移植的。 OTOH,它正在被标准化为fpending

PS:

我只想将stdout和stderr输出定向到日志文件而不是控制台。

根据http://cloud9.hedgee.com./scribbles/daemon#logging,如果你正在编写一个守护进程,那么关闭stdout和stderr并不是一个好理由。 您应该让守护程序管理器(例如守护程序工具,runit,s6,nosh,OpenRC和systemd)处理重定向。

但是,您仍然应该关闭程序最终写入的任何流以检查错误。 从close-stream.c引用:

如果程序向STREAM写入任何内容 ,该程序应该关闭STREAM并确保它在退出之前成功。 否则,假设您最终检查了对STREAM进行显式写入的每个函数的返回状态。 最后一个printf可以成功写入内部流缓冲区,但是当它尝试写出缓冲数据时,fclose(STREAM)仍然可能失败(例如,由于磁盘已满错误)。 因此,您将留下不完整的输出文件,并且违规程序将成功退出。 即使调用fflush也不总是足够的,因为一些文件系统(NFS和CODA)缓冲写入/刷新的数据直到实际的关闭调用。

此外,检查写入STREAM的每个调用的返回值是浪费的 – 只需让内部流状态记录失败。 这就是下面的恐怖测试。