问题
I need to close stdout and stderr for one of my C program. How is it possible without exiting the program in execution?
回答1:
What have you tried? Doesn't fclose
work?
回答2:
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
orstderr
(which areFILE*
pointers (see their definition) then writing to these whilstFILE*
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.
回答3:
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.
回答4:
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.
回答5:
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);
...
来源:https://stackoverflow.com/questions/4972994/how-to-close-stdout-and-stderr-in-c