How to close stdout and stderr in C?

2020-02-26 07:00发布

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
5条回答
等我变得足够好
2楼-- · 2020-02-26 07:43

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.

查看更多
劳资没心,怎么记你
3楼-- · 2020-02-26 07:44

What have you tried? Doesn't fclose work?

查看更多
爷、活的狠高调
4楼-- · 2020-02-26 07:57

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.

查看更多
▲ chillily
5楼-- · 2020-02-26 07:59

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);
...
查看更多
闹够了就滚
6楼-- · 2020-02-26 08:00

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.

查看更多
登录 后发表回答