如何在C中关闭stdout和stderr?

时间:2022-02-11 00:07:12

I need to close stdout and stderr for one of my C program. How is it possible without exiting the program in execution?

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

4 个解决方案

#1


7  

What have you tried? Doesn't fclose work?

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

#2


43  

You can just:

你可以:

fclose(stdout);
fclose(stderr);

For anybody wondering why you might want to do this, this is a fairly common task for a daemon/service process on Unix.

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

However you should be aware that closing a file descriptor may have unintended consequences:

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

  • When you open new files these now free descriptors will be used. So, for example, if you subsequently fopen that file descriptor (on Linux, at least) will replace fd 1, i.e. stdout. Any code that subsequently uses this will write to this file, which may not be what you intended.
  • 当您打开新文件时,将使用这些现在可用的描述符。因此,例如,如果您随后打开该文件描述符(至少在Linux上)将替换fd 1,即stdout。随后使用此代码的任何代码都将写入此文件,这可能与您的意图不同。
  • See R..'s comments on file descriptors versus C library FILE* pointers. Specifically:
    • If you write to a closed fd under Linux, you'll get an error, but:
    • 如果你在Linux下写一个封闭的fd,你会收到一个错误,但是:
    • If you use a C library function that uses stdout or stderr (which are FILE* pointers (see their definition) then writing to these whilst FILE* is closed is undefined behaviour. This will likely crash your program in unexpected ways, not always at the point of the bug either. See undefined behaviour.
    • 如果你使用一个使用stdout或stderr的C库函数(它们是FILE *指针(参见它们的定义),那么在FILE *关闭时写入这些函数是未定义的行为。这可能会以意想不到的方式使程序崩溃,而不是总是在该错误的一点。看到未定义的行为。
  • 请参阅R ..对文件描述符与C库FILE *指针的注释。具体来说:如果你在Linux下写入一个封闭的fd,你会得到一个错误,但是:如果你使用的是一个使用stdout或stderr的C库函数(它们是FILE *指针(参见他们的定义),那么写入这些函数同时写入FILE *已关闭是未定义的行为。这可能会以意想不到的方式崩溃您的程序,而不是总是在错误点。请参阅未定义的行为。
  • Your code isn't the only part affected. Any libraries you use, and any processes you launch which inherited these file descriptors as their standard descriptors are also affected.
  • 您的代码不是唯一受影响的部分。您使用的任何库以及您启动的继承这些文件描述符作为其标准描述符的任何进程也会受到影响。

The quick, one-line solution is to freopen() To say /dev/null, /dev/console under Linux/OSX or nul on Windows. Alternatively, you can use your platform-specific implementation to re-open the file descriptors/handles as required.

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

#3


2  

If you want to prevent your application from writing to the console, then:

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

#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");
}

Outputs:

输出:

stdout: msg1
stderr: msg1
stderr: msg2

Note: any attempt to use a FILE pointer after the file is closed is erroneous. I'm doing it in this case just to illustrate what closing these file descriptors might do to your application.

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

#4


1  

Warning: I am not experienced in C at all, but recently read a slide that answers this question directly by Jim Meyering, a RedHat employee and GNUlib maintainer: https://www.gnu.org/ghm/2011/paris/slides/jim-meyering-goodbye-world.pdf. I merely summarize.

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

TL;DR

Get closeout.c and its dependencies from GNUlib into your source and call

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

atexit(close_stdout);

as your first line in main.

作为你的主要第一行。

Summary

First, some heads up warning, quoting POSIX:

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

Since after the call to fclose() any use of stream results in undefined behavior, fclose() should not be used on stdin, stdout, or stderr except immediately before process termination, ...... If there are any atexit() handlers registered by the application, such a call to fclose() should not occur until the last handler is finishing. Once fclose() has been used to close stdin, stdout, or stderr, there is no standard way to reopen any of these streams.

由于后调用FCLOSE()使用任何未定义的行为流的结果,FCLOSE()不应该在标准输入,标准输出,或标准错误进程终止之前使用,除非立即,......如果有任何的atexit()由应用程序注册的处理程序,在最后一个处理程序完成之前不应该调用fclose()。一旦fclose()用于关闭stdin,stdout或stderr,就没有标准方法可以重新打开任何这些流。

Usage of close() on file descriptors STDIN_FILENO, STDOUT_FILENO or STDERR_FILENO should immediately be followed by an operation to reopen these file descriptors. ...... Furthermore, a close() followed by a reopen operation (e.g. open(), dup() etc) is not atomic; dup2() should be used to change standard file descriptors.

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

Closing stream without handling its errors is not robust, and it is the same for stdout and stderr. Here is a list of errors you need to handle:

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

  • fclose(stdout)
  • FCLOSE(标准输出)
  • ferror(stdout) a.k.a. previous error
  • ferror(stdout)a.k.a。上一个错误
  • __fpending(stdout) a.k.a. stuff not flushed
  • __fpending(stdout)a.k.a。东西没有刷新

Handling these errors, as GNUlib implements in close-stream.c, is quoted below.

正如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;
}

Notice: __fpending is special to glibc and may not be portable. OTOH, it is on the way to be standardized as fpending.

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

P.S.:

P.S:

I just wanted to direct the stdout and stderr output to a log file instead of console.

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

That is not a good reason to close stdout and stderr if you are writing a daemon according to http://cloud9.hedgee.com./scribbles/daemon#logging. You should let a daemon manager (such as daemon tools, runit, s6, nosh, OpenRC and systemd) handle the redirection.

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

However, you still should close any stream that the program has ever written to in the end to check for errors. Quote from close-stream.c:

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

If a program writes anything to STREAM, that program should close STREAM and make sure that it succeeds before exiting. Otherwise, suppose that you go to the extreme of checking the return status of every function that does an explicit write to STREAM. The last printf can succeed in writing to the internal stream buffer, and yet the fclose(STREAM) could still fail (due e.g., to a disk full error) when it tries to write out that buffered data. Thus, you would be left with an incomplete output file and the offending program would exit successfully. Even calling fflush is not always sufficient, since some file systems (NFS and CODA) buffer written/flushed data until an actual close call.

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

Besides, it's wasteful to check the return value from every call that writes to STREAM -- just let the internal stream state record the failure. That's what the ferror test is checking below.

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

#1


7  

What have you tried? Doesn't fclose work?

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

#2


43  

You can just:

你可以:

fclose(stdout);
fclose(stderr);

For anybody wondering why you might want to do this, this is a fairly common task for a daemon/service process on Unix.

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

However you should be aware that closing a file descriptor may have unintended consequences:

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

  • When you open new files these now free descriptors will be used. So, for example, if you subsequently fopen that file descriptor (on Linux, at least) will replace fd 1, i.e. stdout. Any code that subsequently uses this will write to this file, which may not be what you intended.
  • 当您打开新文件时,将使用这些现在可用的描述符。因此,例如,如果您随后打开该文件描述符(至少在Linux上)将替换fd 1,即stdout。随后使用此代码的任何代码都将写入此文件,这可能与您的意图不同。
  • See R..'s comments on file descriptors versus C library FILE* pointers. Specifically:
    • If you write to a closed fd under Linux, you'll get an error, but:
    • 如果你在Linux下写一个封闭的fd,你会收到一个错误,但是:
    • If you use a C library function that uses stdout or stderr (which are FILE* pointers (see their definition) then writing to these whilst FILE* is closed is undefined behaviour. This will likely crash your program in unexpected ways, not always at the point of the bug either. See undefined behaviour.
    • 如果你使用一个使用stdout或stderr的C库函数(它们是FILE *指针(参见它们的定义),那么在FILE *关闭时写入这些函数是未定义的行为。这可能会以意想不到的方式使程序崩溃,而不是总是在该错误的一点。看到未定义的行为。
  • 请参阅R ..对文件描述符与C库FILE *指针的注释。具体来说:如果你在Linux下写入一个封闭的fd,你会得到一个错误,但是:如果你使用的是一个使用stdout或stderr的C库函数(它们是FILE *指针(参见他们的定义),那么写入这些函数同时写入FILE *已关闭是未定义的行为。这可能会以意想不到的方式崩溃您的程序,而不是总是在错误点。请参阅未定义的行为。
  • Your code isn't the only part affected. Any libraries you use, and any processes you launch which inherited these file descriptors as their standard descriptors are also affected.
  • 您的代码不是唯一受影响的部分。您使用的任何库以及您启动的继承这些文件描述符作为其标准描述符的任何进程也会受到影响。

The quick, one-line solution is to freopen() To say /dev/null, /dev/console under Linux/OSX or nul on Windows. Alternatively, you can use your platform-specific implementation to re-open the file descriptors/handles as required.

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

#3


2  

If you want to prevent your application from writing to the console, then:

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

#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");
}

Outputs:

输出:

stdout: msg1
stderr: msg1
stderr: msg2

Note: any attempt to use a FILE pointer after the file is closed is erroneous. I'm doing it in this case just to illustrate what closing these file descriptors might do to your application.

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

#4


1  

Warning: I am not experienced in C at all, but recently read a slide that answers this question directly by Jim Meyering, a RedHat employee and GNUlib maintainer: https://www.gnu.org/ghm/2011/paris/slides/jim-meyering-goodbye-world.pdf. I merely summarize.

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

TL;DR

Get closeout.c and its dependencies from GNUlib into your source and call

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

atexit(close_stdout);

as your first line in main.

作为你的主要第一行。

Summary

First, some heads up warning, quoting POSIX:

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

Since after the call to fclose() any use of stream results in undefined behavior, fclose() should not be used on stdin, stdout, or stderr except immediately before process termination, ...... If there are any atexit() handlers registered by the application, such a call to fclose() should not occur until the last handler is finishing. Once fclose() has been used to close stdin, stdout, or stderr, there is no standard way to reopen any of these streams.

由于后调用FCLOSE()使用任何未定义的行为流的结果,FCLOSE()不应该在标准输入,标准输出,或标准错误进程终止之前使用,除非立即,......如果有任何的atexit()由应用程序注册的处理程序,在最后一个处理程序完成之前不应该调用fclose()。一旦fclose()用于关闭stdin,stdout或stderr,就没有标准方法可以重新打开任何这些流。

Usage of close() on file descriptors STDIN_FILENO, STDOUT_FILENO or STDERR_FILENO should immediately be followed by an operation to reopen these file descriptors. ...... Furthermore, a close() followed by a reopen operation (e.g. open(), dup() etc) is not atomic; dup2() should be used to change standard file descriptors.

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

Closing stream without handling its errors is not robust, and it is the same for stdout and stderr. Here is a list of errors you need to handle:

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

  • fclose(stdout)
  • FCLOSE(标准输出)
  • ferror(stdout) a.k.a. previous error
  • ferror(stdout)a.k.a。上一个错误
  • __fpending(stdout) a.k.a. stuff not flushed
  • __fpending(stdout)a.k.a。东西没有刷新

Handling these errors, as GNUlib implements in close-stream.c, is quoted below.

正如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;
}

Notice: __fpending is special to glibc and may not be portable. OTOH, it is on the way to be standardized as fpending.

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

P.S.:

P.S:

I just wanted to direct the stdout and stderr output to a log file instead of console.

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

That is not a good reason to close stdout and stderr if you are writing a daemon according to http://cloud9.hedgee.com./scribbles/daemon#logging. You should let a daemon manager (such as daemon tools, runit, s6, nosh, OpenRC and systemd) handle the redirection.

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

However, you still should close any stream that the program has ever written to in the end to check for errors. Quote from close-stream.c:

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

If a program writes anything to STREAM, that program should close STREAM and make sure that it succeeds before exiting. Otherwise, suppose that you go to the extreme of checking the return status of every function that does an explicit write to STREAM. The last printf can succeed in writing to the internal stream buffer, and yet the fclose(STREAM) could still fail (due e.g., to a disk full error) when it tries to write out that buffered data. Thus, you would be left with an incomplete output file and the offending program would exit successfully. Even calling fflush is not always sufficient, since some file systems (NFS and CODA) buffer written/flushed data until an actual close call.

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

Besides, it's wasteful to check the return value from every call that writes to STREAM -- just let the internal stream state record the failure. That's what the ferror test is checking below.

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