如何在C中关闭stdout和stderr?

时间:2011-02-11 19:06:48

标签: c stdout stderr

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

5 个答案:

答案 0 :(得分:45)

你可以:

fclose(stdout);
fclose(stderr);

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

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

  • 当您打开新文件时,将使用这些免费的描述符。因此,例如,如果您随后fopen该文件描述符(至少在Linux上)将替换fd 1,即stdout。随后使用此代码的任何代码都将写入此文件,这可能不是您的意图。
  • 请参阅R ..对文件描述符与C库FILE*指针的评论。特别:
    • 如果您在Linux下写入已关闭的fd,则会收到错误,但是:
    • 如果您使用的C库函数使用stdoutstderrFILE*指针(请参阅their definition),请在FILE*时写入/dev/null关闭是未定义的行为。这可能会以意想不到的方式使您的程序崩溃,而不是总是在错误点。请参阅undefined behaviour
  • 您的代码不是唯一受影响的部分。您使用的任何库以及您启动的继承这些文件描述符作为标准描述符的任何进程也会受到影响。

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

答案 1 :(得分:8)

你有什么尝试? fclose不起作用吗?

答案 2 :(得分:2)

如果您想阻止您的应用程序写入控制台,那么:

#include <stdio.h>

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指针都是错误的。我在这种情况下这样做只是为了说明关闭这些文件描述符可能对你的应​​用程序做什么。

答案 3 :(得分:2)

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

TL; DR

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

atexit(close_stdout);

作为主要的第一行。

摘要

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

  

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

     

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

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

  • fclose(stdout)
  • ferror(stdout) a.k.a.上一个错误
  • __fpending(stdout) a.k.a.东西没有刷新

下面引用了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 a b >&-' (i.e., 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来说是特殊的,可能无法移植。 OTOH,on the way to be standardizedfpending

P.S:

  

我只想将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 - 只是让内部流状态记录      失败。这就是下面的恐怖测试。

答案 4 :(得分:0)

实际上,您也可以使用close函数:

#include<unistd.h> // close
#include<stdio.h>  // STDOUT_FILENO,STDERR_FILENO
...
close(STDOUT_FILENO);
close(STDERR_FILENO);
...