1

I am writing a C program on unix which should redirect it's output to the file, and write to it some text every second in infinite loop:

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
    int outDes = open("./output.txt", O_APPEND | O_WRONLY);
    dup2(outDes, 1);
    while(1) {
        printf("output text\n");
        sleep(1);
    }
}

But it writes nothing to the output file. I tried to change the 'while' loop for 'for' with 10 loops, and I found that it writes all 10 lines to the file at once after the series ends. It is not very good for me, while I need to have an infinite loop.

When I'm not redirecting output, it is all ok, and new line appears every second on terminal.

I also tried to put one

printf("text\n");

before redirecting output to the file. Then the program wrote the lines to the file in real time, which is good, but wrote there the first (non redirected) line too. I don't want this first line in my output file, I don't understand how it could be written into file when output was not redirected yet (maybe redirect remained there since last run?), and how it could cause that the lines are suddenly written in real time.

Can anyone explain me how does it work?

2 Answers 2

5
  1. You are not checking the return value of open() and dup2(). If either open() or dup2() failed, it won't write anything in output.txt.

    if (outDes < -1) {
        perror("open");
        return 1;
    }
    if (dup2(outDes, 1) == -1) {
        perror("dup2");
        return 1;
    }
    
  2. stdio streams are buffered, and the writes happen in memory before being done on the real file description.

    Try adding a fflush(stdout) after printf().

Sign up to request clarification or add additional context in comments.

3 Comments

That's what I thought too, but the OP is printint a "\n" so it should flush.
@Chris: Some Unix C libraries decide whether or not stdout is a terminal (and therefore whether or not to line-buffer it) on the first write. By swapping file descriptor 1 before the first write, the OP reveals to the library that it's not a terminal.
@Zach - That's tremendously clever.
0

You're running afoul of a poorly documented DWIMmy feature in many Unix C libraries. The first time you write to stdout or stderr, the library probes the underlying file descriptor (with isatty(3)). If it's a (pseudo-)terminal, the library puts the FILE in "line buffered" mode, meaning that it'll buffer input until a newline is written and then flush it all to the OS. But if the file descriptor is not a terminal, it puts the FILE in "fully buffered" mode, where it'll buffer something like BUFSIZ bytes of output before flushing them, and pays no attention to line breaks.

This is normally the behavior you want, but if you don't want it (as in this case), you can change it with setvbuf(3). This function (although not the behavior I described above) is ISO standard C. Here's how to use it in your case.

#include <stdio.h>
#include <unistd.h>

int
main(void)
{
    if (freopen("output.txt", "a", stdout)) {
        perror("freopen");
        return 1;
    }
    if (setvbuf(stdout, 0, _IOLBF, 0)) {
        perror("setvbuf");
        return 1;
    }

    for (;;) {
        puts("output text");
        sleep(1);
    }
    /* not reached */
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.