dragosht
dragosht

Reputation: 3275

Trouble using dup2 to redirect stdout/stderr into the same file

I'm having some trouble using dup2 in trying to redirect both stdout and stderr into the same output file.

I'm using this explanatory code sample: (gcc 4.8.2, Ubuntu 14.04)

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>

#define USE2FILES

int main()
{
    int f1, f2, status;

    f1 = open("test.out", O_CREAT | O_WRONLY, 0644);
    if (f1 == -1) {
        perror("open(): ");
    }
    status = dup2(f1, STDOUT_FILENO);
    if (status == -1) {
        perror("dup2(): ");
    }

#ifdef USE2FILES
    close(f1);
#endif

#ifdef USE2FILES
    f2 = open("test.out", O_CREAT | O_WRONLY, 0644);
    if (f2 == -1) {
        perror("dup2(): ");
    }
#else
    f2 = f1;
#endif
    status = dup2(f2, STDERR_FILENO);
    if (status == -1) {
        perror("dup2(): ");
    }
    close(f2);

    fprintf(stderr, "test_stderr1\n");
    fprintf(stdout, "test_stdout1\n");
    fprintf(stderr, "test_stderr2\n");
    fprintf(stdout, "test_stdout2\n");
    fprintf(stderr, "test_stderr3\n");
    fprintf(stdout, "test_stdout3\n");

    fflush(stdout);
    fflush(stderr);

    return 0;
}

USE2FILES macro is supposed to switch between using either 2 file descriptors (to the same file) which get duped to stdout and stderr respectivly or 1 file descriptor which gets duplicated both to stdout and stderr.

I was under the impression that using 2 distinct file descriptors for redirection should work. However running this piece of code with USE2FILES on issues the following output in test.out:

test_stdout1
test_stdout2
test_stdout3

If I then disable USE2FILES I get:

test_stderr1
test_stderr2
test_stderr3
test_stdout1
test_stdout2
test_stdout3

Seems like in the first case no output towards stderr gets through. Is this behavior to be expected (am I missing something)?

EDIT: After accepted Chris Dodd's answer: That's indeed a poor example. Changing the fprintf sequence to something like this:

    fprintf(stderr, "test_stderr+++++++++++++++++++++++++++++++++++++++++++++++++1\n");
    fprintf(stdout, "test_stdout----------------------------------------1\n");
    fprintf(stderr, "test_stderr++++++++++++++++++++++++++++++++++2\n");
    fprintf(stdout, "test_stdout----------------2\n");
    fprintf(stderr, "test_stderr++++++++++++++++++++++++++++3\n");
    fprintf(stdout, "test_stdout----------------------3\n");

gets me this test.out output:

test_stdout----------------------------------------1
test_stdout----------------2
test_stdout----------------------3
err++++++++++++++++++++++++++++3

showing pretty clearly stdout & stderr are competing with their writes over the same file.

Upvotes: 1

Views: 2468

Answers (1)

Chris Dodd
Chris Dodd

Reputation: 126498

If you do two open calls, you get two distinct kernel filehandles, each with its own I/O cursor (file offset), so writes to the two file descriptors will overwrite each other. If you use a single open call, you only get a single filehandle that both file descriptors refer to, so each write (to each descriptor) will advance the output offset so the next write (with the other file descriptor) will write after it.

In your example, the strings written are the exact same length, so the write to stdout exactly overwrites the preceeding write to stderr. Note that the file write only occurs when the FILE object is flushed, not (necessarily) when fprintf is called.

You could also get the effect you seem to be trying to get by opening the files in O_APPEND mode. This will cause every write to reposition the write offset to the current end of the file just before actually writing.

Upvotes: 5

Related Questions