2

In my program in C, running on Linux, which creates sub-processes using system() I noticed that when I redirected stdout to a pipe or to a file then the output of the sub-processes was sent before the output of buffered I/O functions like printf(). When stdout was left to go to the terminal then the output was in the expected order. I simplified the program to the following example:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    printf("1. output from printf()\n");
    system("echo '2. output from a command called using system()'");

    return EXIT_SUCCESS;
}

The expected output when stdout goes to the terminal:

$ ./iobuffer
1. output from printf()
2. output from a command called using system()

The output out of order when stdout is redirected to a pipe or a file:

$ ./iobuffer | cat
2. output from a command called using system()
1. output from printf()
  • possible duplicate of [Why does stdout need explicit flushing when redirected to file?](http://stackoverflow.com/questions/13932932/why-does-stdout-need-explicit-flushing-when-redirected-to-file) – Martijn Pieters Jul 03 '13 at 09:47
  • Unfortunately I did not find the similar question [Why does stdout need explicit flushing when redirected to file?](http://stackoverflow.com/questions/13932932/why-does-stdout-need-explicit-flushing-when-redirected-to-file) though I tried multiple searches. I encountered the problem recently and I already knew the answer when I was writing the question. I thought that this question is missing here. Probably it is still useful as my searches did not work :) I had an answer prepared but I did not know that I will not be allowed to answer my question immediately :( – pabouk - Ukraine stay strong Jul 03 '13 at 10:13
  • There is a checkbox on the 'Ask a Question' page, that opens a answer text box too. You can then post both the question *and* the answer in one go. If you post *just* a question, you have to wait a bit. – Martijn Pieters Jul 03 '13 at 10:18

2 Answers2

5

A terminal generally uses line-buffering, while a pipe would use block buffering.

This means that your printf call, which includes a newline, will fill the line buffer, triggering a flush. When redirecting, no flush takes place until the program completes.

echo on the other hand, always flushes the buffer it is writing to when it completes.

With line buffering (terminal output), the order is:

  • printf() prints a line with newline, buffer is flushed, you see 1. output from printf() being printed.
  • echo writes output, exits, flushes the buffer, you see 2. output from a command called using system() printed.

With block buffering, the order is:

  • printf() prints a line with newline, not fully filling the buffer.
  • echo writes output, exits, flushes the buffer, you see 2. output from a command called using system() printed.
  • Your program exits, flushes its block buffer, you see 1. output from printf() being printed.

Your options are to use to flush explicitly using fflush() or to set the buffering on stdout explicitly with setvbuf().

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thank you for your unbelievably quick answer. In fact I knew the answer already but I was not allowed to add it immediately. Is it worth to add my answer complete or just the information which is not covered in your answer? --- To your answer: There is a minor mistake - line buffering can be set only with `setvbuf()` not `setbuf()`. Also I do not understand how `fprintf()` can bypass the buffering. AFAIK it uses exactly the same buffering as `printf()`. – pabouk - Ukraine stay strong Jul 03 '13 at 13:15
  • 1
    I removed the `fprintf()` claim, I misinterpreted another piece of info. I corrected the `setvbuf()` call (typo there). You are always free to add your own answer and let future visitors to this page decide with voting what information they found to be useful. – Martijn Pieters Jul 03 '13 at 13:20
2

This reply is complementing the reply of Martijn Pieters. Citations of sources describing the default buffering modes of streams are at the end.

Solutions

Here are three basic solutions with explanation:

a) Flush the buffers before calling a sub-process. This option could be significantly more effective when you do a lot of output from the main program and just few sub-process calls.

printf("1. output from printf()\n");
fflush(stdout);
system("echo '2. output from a command called using system()'");

b) Change the buffering of stdout to line-buffering (or unbuffered) for the whole program. This option is a small change to the program as you call sevbuf() only at the beginning and the rest of the program stays the same.

if(setvbuf(stdin, NULL, _IOLBF, BUFSIZ))
    err(EXIT_FAILURE, NULL);
printf("1. output from printf()\n");
system("echo '2. output from a command called using system()'");

Edit:
c) Change the buffering of stdout to line-buffering (or unbuffered) for the whole program by an external utility. This option does not change the program at all so you do not need to recompile or even have sources of the program. You just call the program using the stdbuf utility.

$ stdbuf -oL ./iobuffer | cat
1. output from printf()
2. output from a command called using system()

References - describing why the buffering mode changes

The initial buffering setting is described for example in the documents below. Streams to interactive devices like terminal are by default line-buffered so that newline-ended messages appear immediately on the terminal. Pipes, files etc. use block-buffering (or full-buffering) for better performance.

The GNU C Library Reference Manual
http://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html#Buffering-Concepts

Newly opened streams are normally fully buffered, with one exception: a stream connected to an interactive device such as a terminal is initially line buffered.

Linux man-pages: stdin (3)
http://linux.die.net/man/3/stdin

The stream stderr is unbuffered. The stream stdout is line-buffered when it points to a terminal. Partial lines will not appear until fflush(3) or exit(3) is called, or a newline is printed. This can produce unexpected results, especially with debugging output. The buffering mode of the standard streams (or any other stream) can be changed using the setbuf(3) or setvbuf(3) call.

There are also a mention of the buffering of a terminal driver.

ISO/IEC 9899:201x C11 Committee Draft — April 12, 2011; 7.21.3 Files, page 301
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf

As initially opened, the standard error stream is not fully buffered; the standard input and standard output streams are fully buffered if and only if the stream can be determined not to refer to an interactive device.

The Open Group: System Interfaces and Headers Issue 4, Version 2; 2.4 Standard I/O Streams, page 32
https://www2.opengroup.org/ogsys/catalog/C435 (free registration needed for download)

When opened, the standard error stream is not fully buffered; the standard input and standard output streams are fully buffered if and only if the stream can be determined not to refer to an interactive device.

There is also a very interesting chapter 2.4.1 "Interaction of File Descriptors and Standard I/O Streams" about combining of buffered and unbuffered I/O which somewhat relates to sub-process calls.