如何在C中关闭stdout和stderr?

发布于 2024-10-17 09:40:07 字数 48 浏览 8 评论 0原文

我需要关闭我的 C 程序之一的标准输出和标准错误。正在执行的程序怎么可能不退出呢?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

少跟Wǒ拽 2024-10-24 09:40:08

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

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

Actually you can also use the close function:

#include<unistd.h> // close
#include<stdio.h>  // STDOUT_FILENO,STDERR_FILENO
...
close(STDOUT_FILENO);
close(STDERR_FILENO);
...
天煞孤星 2024-10-24 09:40:08

您可以尝试一下有关标准输出的内容。它不会关闭 stdout,但会将 stdout 重定向到 NULL。我相信你也可以对 stderr 做同样的事情。它在我的情况下有效(Linux/GCC)(它应该在 Unix 和 Windows 中都有效,但我还没有在 Windows 中进行测试)。

//saving current stdout descriptor
FILE oldstdout = *stdout;
//open a output stream to null
FILE *mystdout = fopen("/dev/null", "w"); //to unix
if( !mystdout )        
    mystdout = fopen("NUL", "w"); // to Windows

*stdout = *mystdout; //replacing stdout by our stream to null

/*******  PUT YOUR CODE HERE TO BE "MUTED" *******/

//enabling back stdout 
*stdout = oldstdout;
fclose(mystdout);   //closing my null stream

这个解决方案的优点是标准输出永远不会关闭。因此,可以根据需要随时恢复原始标准输出。我们也不会有将 stdout 文件描述符分配给另一个输出流的任何风险。

You can try this about stdout. It will not close stdout, but it will redirect stdout to NULL. I believe you could do the same for stderr. It worked in my situation (Linux/GCC) (It should works in both Unix and Windows, but I have not test in Windows).

//saving current stdout descriptor
FILE oldstdout = *stdout;
//open a output stream to null
FILE *mystdout = fopen("/dev/null", "w"); //to unix
if( !mystdout )        
    mystdout = fopen("NUL", "w"); // to Windows

*stdout = *mystdout; //replacing stdout by our stream to null

/*******  PUT YOUR CODE HERE TO BE "MUTED" *******/

//enabling back stdout 
*stdout = oldstdout;
fclose(mystdout);   //closing my null stream

The advantage of this solution is stdout is never closed. So, the original stdout can be restored at any time as it is necessary. We also do not have any risk of stdout file descriptor be assigned to another output stream.

烟织青萝梦 2024-10-24 09:40:07

您可以:

fclose(stdout);
fclose(stderr);

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

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

  • 当您打开新文件时,将使用这些现在空闲的描述符。因此,例如,如果您随后 fopen 该文件描述符(至少在 Linux 上)将替换 fd 1,即 stdout。随后使用此文件的任何代码都将写入此文件,这可能不是您想要的。
  • 请参阅 R.. 关于文件描述符与 C 库 FILE* 指针的注释。具体来说:
    • 如果您在 Linux 下写入关闭的 fd,您会收到错误消息,但是:
    • 如果您使用的 C 库函数使用 stdoutstderr(它们是 FILE* 指针(请参阅 它们的定义)然后在 FILE* 关闭时写入这些内容是未定义的这可能会以意想不到的方式使您的程序崩溃,并不总是在错误发生时参见未定义的行为。 .
  • 您的代码并不是唯一受影响的部分。您使用的任何库以及您启动的继承这些文件描述符的任何进程因为它们的标准描述符也会受到影响,

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

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.

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.
  • 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:
    • 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.
  • 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.

離殇 2024-10-24 09:40:07

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

What have you tried? Doesn't fclose work?

残花月 2024-10-24 09:40:07

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

#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 指针的尝试都是错误的。在本例中,我这样做只是为了说明关闭这些文件描述符可能会对您的应用程序造成什么影响。

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.

债姬 2024-10-24 09:40:07

警告:我根本没有 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);

作为 main 中的第一行进行调用。

摘要

首先,一些警告,引用 POSIX

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

在文件描述符 STDIN_FILENO、STDOUT_FILENO 或 STDERR_FILENO 上使用 close() 后,应立即执行重新打开这些文件描述符的操作。 ……此外,close() 后跟重新打开操作(例如 open()、dup() 等)不是原子的; 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 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,正在标准化 作为fpending

附:

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

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

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

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

此外,检查每次调用的返回值也是浪费的
写入STREAM——只是让内部流状态记录
失败。这就是下面的恐惧测试要检查的内容。

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.

TL;DR

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

atexit(close_stdout);

as your first line in main.

Summary

First, some heads up warning, quoting 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.

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.

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:

  • fclose(stdout)
  • ferror(stdout) a.k.a. previous error
  • __fpending(stdout) a.k.a. stuff not flushed

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

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.

P.S.:

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

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.

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:

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文