Arjob Mukherjee
Arjob Mukherjee

Reputation: 397

Redirection operation writes output of order in file

I have a simple C file, that writes to the standard output using printf and write(1,..) functions.

File: main.c

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

int main()
{
    printf("Hello\n");
    printf("Mars\n");
    fsync(1);
    write(1,"Ola\n",4);
    write(1,"Earth\n",6);
}

> gcc main.c -o test

When I redirect the output to a file, I see different order than the output in the terminal. It seems, that when I am using redirection write(..) writes before than printf(..), even tough, printf() is earlier in code and there is a fsync(..) in between.

Is there some variant of the redirection operator that will ensure order, or am I doing something wrong.

Output in terminal:

> ./test

Hello
Mars
Ola
Earth

Output in out file:

> ./test >out
# or ./test|tee out

> cat out

Ola
Earth
Hello
Mars

Upvotes: 1

Views: 161

Answers (2)

Eric Postpischil
Eric Postpischil

Reputation: 224052

printf writes to the C stream stdout, which is buffered by the C standard library routines and ultimately written to Unix file descriptor 1.

fsync and write operate on Unix file descriptor 1. fsync flushes data from file descriptor 1, but it has no effect on the buffers of the streams of the C standard library.

The C standard library I/O routines operate differently depending on whether standard output is going to a terminal or to a file. This is detected during program start-up. When output is going to a terminal, it is deemed important to get it to the user, and the buffer is flushed when \n is written to a stream. This is called line buffered, and output to a terminal is set to line buffered by default.

When output is going to a file, it is not deemed important to flush it promptly, since a user is not expected to see it right away and performance is better if output is fully buffered. So the buffer is flushed only when it is full, the stream is closed, or an explicit flush request is made. This is called fully buffered, and output to a file is set to fully buffered by default.

This is generally addressed in C 2018 7.21.3, in which paragraph 7 says:

… 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.

(When the output is a terminal, the C implementation may also use unbuffered mode instead of line buffered, in which case all characters are sent to the terminal as soon as they are written. This is uncommon.)

You can explicitly requesting flushing of stdout with fflush(stdout).

After stdout is opened but before any other operation is performed on it, you can change its buffer method with setvbuf(stdout, NULL, mode, 0), where mode is _IOFBF, _IOLBF, or _IONBF for full, line, or no buffering, respectively. (You can also pass a char array and its size in place of NULL and 0, and the library will use that array for the buffer, after which you should not use the array for any other purpose. If you pass NULL and a size, the library may use that size to perform its own buffer allocation, but that is not guaranteed.) setvbuf returns an int that may be non-zero if it cannot honor the request.

Upvotes: 4

Benjamim Coelho
Benjamim Coelho

Reputation: 67

have a look at the dup2 system call: https://www.geeksforgeeks.org/dup-dup2-linux-system-call/

you can redirect stdoutput / input to a file

Upvotes: -1

Related Questions