How to write to stderr so people will like you

April 16, 2010

Suppose that you're writing a command like, say, make; it writes progress output to standard output and error reports to standard error, and it doesn't necessarily immediately terminate when it encounters an error (or prints a warning; the important thing is that it keeps running after it prints things to standard error).

There's a subtle catch in how you want to print things (such as standard error messages) to stderr in such a program. You don't want to just do the straightforward thing of printing to stderr and being done with it. Instead, you want to print messages to stderr with the following sequence: flush stdout, write your message to stderr, then flush stderr.

(This is obviously inapplicable if all of your IO is complete unbuffered and there is nothing to flush. But this is an uncommon case.)

To convince you of this, here's a little test program to illustrate what goes wrong if you don't:

$ cat tst.c
#include    <stdio.h>
int main(int argc, char **argv)
{
    printf("line 1\n");
    fprintf(stderr, "stderr line 1\n");
    printf("line 2\n");
    fprintf(stderr, "stderr line 2\n");
}

And now let's show what goes wrong:

$ ./tst >/tmp/logit 2>&1; cat /tmp/logit
stderr line 1
stderr line 2
line 1
line 2

(Note that these results are system and language dependent; your choices may behave differently.)

That's not exactly the output that you'd expect or that you want, seeing as the error messages have completely been relocated out of their actual context.

In most implementations of buffered IO, interactive IO is line buffered (it is written out to you after every newline) while IO to anything else is block buffered, generally in reasonable size blocks. The problem is that when you redirect the output of this program into a file, stdout is buffered separately from stderr even though they have been redirected to the same place. As a result, at the end of tst you get all of the stdout messages flushed out in one block, not interleaved with the stderr messages.

(Note that on some systems and in some languages stderr is always line buffered or unbuffered, even when it is being written to files.)

Doing the flush dance defeats this separate buffering and so makes stdout and stderr be properly interleaved even when redirected into a file. Note that it may not sufficient to just flush stdout before writing to stderr, because if stderr is block buffered there's nothing that prevents subsequent messages to stdout being flushed out before the stderr message.

(Note that programs that run other programs need to do more than just this; they need to flush stdout and perhaps stderr before they start another program.)


Comments on this page:

From 99.135.97.120 at 2010-04-17 00:05:23:

Wouldn't it be useful to actually provide the thrilling conclusion by showing the correct example with an fflush?

Written on 16 April 2010.
« The standard format Unix error messages
The 30 second elevator pitch on HTML 4.01 (vs XHTML) »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Fri Apr 16 01:17:54 2010
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.