34
#include <stdio.h>

int main() {
    printf("This goes to screen\n");
    freopen("out.txt", "a", stdout);
    printf("This goes to out.txt");
    freopen("/dev/stdout", "a", stdout);
    printf("This should go to screen too, but doesn't\n");

    return 0;
}

I call freopen to redirect the stdout to out.txt then I print something on the file, now I want to redirect it back to the screen, but freopen("/dev/stdout", "a", stdout); doesn't work. Is there any way to do that using ANSI C or POSIX system calls?

Hoffmann
  • 14,369
  • 16
  • 76
  • 91

6 Answers6

23

Unfortunately, there doesn't seem to be a good way:

http://c-faq.com/stdio/undofreopen.html

The best recommendation is not to use freopen in this circumstance.

Andy West
  • 12,302
  • 4
  • 34
  • 52
23

I can't think of a way to do this in a cross-platform manner, but on GNU/Linux systems (and maybe other POSIX-compliant ones, too) you can freopen ("/dev/tty", "a", stdout). Is this what you were trying to do?

Patrick Niedzielski
  • 1,194
  • 1
  • 8
  • 21
  • 13
    This will break things in case of redirection or piping to another process. – Michael Krelin - hacker Dec 15 '09 at 19:07
  • Only the last process that I fork() needs to redirect to the screen. So this works for my case. – Hoffmann Dec 15 '09 at 20:30
  • 4
    Hoffman, of course it may be good enough for your particular case, so I'm putting this comments for those who will find the question and may care about the case when the whole thing is redirected or has no tty at all. – Michael Krelin - hacker Dec 15 '09 at 23:01
  • I agree, hacker. I like Jonathan Leffler's solution below, in which we has a wrapper for printf and a default stream. A very nice solution, and cross platform (as far as I can tell), too. – Patrick Niedzielski Dec 17 '09 at 20:57
16

Generally speaking, you can't. You have closed the file, which could've been pipe or whatever. It's not reopenable. You might have saved stdout value, then assign some fopen to it and then close it and copy the old value back. Example:

FILE *o = stdout;
stdout=fopen("/tmp/crap.txt","a");
printf("Oh no!\n");
fclose(stdout);
stdout = o;

Mike Weller suggested below in comments that stdout might not always be writable. In this case something like that might help:

int o = dup(fileno(stdout));
freopen("/tmp/crap.txt","a",stdout);
printf("Oh no!\n");
dup2(o,fileno(stdout));
close(o);

Another edit: if you're using it to redirect output from the child process like your comment elsewhere suggest, you can redirect it after the fork.

ryry1985
  • 336
  • 5
  • 11
Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
15

Use fdopen() and dup() as well as freopen().

int old_stdout = dup(1);  // Preserve original file descriptor for stdout.

FILE *fp1 = freopen("out.txt", "w", stdout);  // Open new stdout

...write to stdout...   // Use new stdout

FILE *fp2 = fdopen(old_stdout, "w");   // Open old stdout as a stream

...Now, how to get stdout to refer to fp2?
...Under glibc, I believe you can use:

fclose(stdout);    // Equivalent to fclose(fp1);
stdout = fp2;      // Assign fp2 to stdout
// *stdout = *fp2;   // Works on Solaris and MacOS X, might work elsewhere.

close(old_stdout);   // Close the file descriptor so pipes work sanely

I'm not sure whether you can do the assignment reliably elsewhere.

Dubious code that does actually work

The code below worked on Solaris 10 and MacOS X 10.6.2 - but I'm not confident that it is reliable. The structure assignment may or may not work with Linux glibc.

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

int main(void)
{
    printf("This goes to screen\n");
    int old_stdout = dup(1);  // Consider dup(STDOUT_FILENO) or dup(fileno(stdout))
    FILE *fp1 = freopen("out.txt", "a", stdout);
    printf("This goes to out.txt\n");
    fclose(stdout);
    FILE *fp2 = fdopen(old_stdout, "w");
    *stdout = *fp2;                       // Unreliable!
    printf("This should go to screen too, but doesn't\n");

    return 0;
}

You can't say you weren't warned — this is playing with fire!

If you're on a system with the /dev/fd file system, you could create the name of the file implied by the file descriptor returned from dup() with sprintf(buffer, "/dev/fd/%d", old_stdout) and then use freopen() with that name. This would be a lot more reliable than the assignment used in this code.

The better solutions either make the code use 'fprintf(fp, ...)' everywhere, or use a cover function that allows you set your own default file pointer:

mprintf.c

#include "mprintf.h"
#include <stdarg.h>

static FILE *default_fp = 0;

void set_default_stream(FILE *fp)
{
    default_fp = fp;
}

int mprintf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    if (default_fp == 0)
        default_fp = stdout;

    int rv = vfprintf(default_fp, fmt, args);

    va_end(args);
    return(rv);
 }

mprintf.h

#ifndef MPRINTF_H_INCLUDED
#define MPRINTF_H_INCLUDED

#include <stdio.h>

extern void set_default_stream(FILE *fp);
extern int  mprintf(const char *fmt, ...);

#endif

Clearly, you can create an mvprintf() and other functions as needed.

Example use of mprintf()

Then, in place of the original code, you can use:

#include "mprintf.h"

int main()
{
    mprintf("This goes to screen\n");
    FILE *fp1 = fopen("out.txt", "w");
    set_default_stream(fp1);
    mprintf("This goes to out.txt\n");
    fclose(fp1);
    set_default_stream(stdout);
    mprintf("This should go to screen too, but doesn't\n");

    return 0;
}

(Warning: untested code - confidence level too high. Also, all code written assuming you use a C99 compiler, primarily because I declare variables when I first need them, not at the beginning of the function.)


Caution:

Note that if the original program is invoked as ./original_program > file or ./original_program | grep something (with redirected output) or is run from a cron job, then opening /dev/tty is not usually appropriate as a way to reopen standard output because the original standard output was not the terminal.

Also, note that if the redirection of standard output is used prior to forking and execing a child program and the original standard output is reinstated in the parent, then the sequence of operations is wrong. You should fork and then adjust the I/O of the child (only), without modifying the parent's I/O at all.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Your first suggestion seems to be the right way to do it, but Patrick's suggestion is a lot simpler and does what I need. I did not test your answer but I think it should work on linux too. Thanks for the answer though. fprintf(fp,...) is not an option for me because I use exec() on the child process. – Hoffmann Dec 15 '09 at 20:36
  • 1
    Hoffman, if you're using exec (presumably you first fork, if it's a child process) then why not do the redirection *after* you forked? – Michael Krelin - hacker Dec 15 '09 at 23:04
  • 1
    This is actually a really nice solution, Hoffmann. You could even simplify this down with a variable argument macro to wrap printf. This would only take one line. – Patrick Niedzielski Dec 17 '09 at 20:55
  • The second suggestion, that is. – Patrick Niedzielski Dec 17 '09 at 20:55
  • The first suggestion also works on Linux (Ubuntu 16 LTS) – CharlesB Jan 16 '18 at 18:06
  • `error: incomplete type 'FILE' (aka 'struct __sFILE') is not assignable` – evandrix Feb 21 '19 at 07:38
  • @evandrix: then it's a good job the code I wrote only assigns to `FILE *` (file pointers). How did you modify it so that your version is assigning a complete `FILE`, which is usually a structure type, and sometimes is an incomplete structure type and hence cannot be assigned. (Which example fragment are you encountering that problem in? I don't think it is the `mprintf()` example. If you're referring to the **dubious** example, what you're encountering is one of many reasons that it is dubious. _[…continued…]_ – Jonathan Leffler Feb 21 '19 at 07:48
  • _[…continuation…]_ There's also the quote from [C11 §7.21.3 Files ¶6](http://port70.net/~nsz/c/c11/n1570.html#7.21.2): _There's also: _The address of the `FILE` object used to control a stream may be significant; a copy of a `FILE` object need not serve in place of the original._ You were warned that the fragment is dubious; now you know some of the reasons why. – Jonathan Leffler Feb 21 '19 at 07:50
5

On Windows, you can open "CONOUT$".

freopen("test.txt", "w", stdout);
printf("this goes to test.txt");
freopen("CONOUT$", "w", stdout);
printf("this goes to the console\n");

This probably doesn't work if stdout is redirected to start with.

user253751
  • 57,427
  • 7
  • 48
  • 90
  • 2
    Your solution didn't allow me to restore stderr. [This article about _dup](http://msdn.microsoft.com/en-us/library/8syseb29.aspx) works for me. – Brandlingo Oct 22 '14 at 15:04
  • @cegprakash Did you open CONIN$ for stdin? – user253751 Mar 29 '16 at 19:38
  • I am just using freopen("test1.txt","r",stdin); and after sometime I want to do freopen("test2.txt","r",stdin) But stdin still points to the first file. – cegprakash Apr 01 '16 at 10:05
2

The following code (SwapIOB) is used in Testbenches that want to store the stdout stream for comparison to an expected results file.

Background: File streams are managed using an _IOB structure that is stored in an array of 20 _IOB entries. This includes stdout stream. The IOBs are stored in an array. When a file is created the application code gets a ptr to an element in that array. The application code then passes that ptr to the OS for processing I/O calls. Thus, the OS does NOT itself contain or rely on its own pointers to the application's IOB.

Requirement: When running a testbench the stdout messages issued by an application should be re-directed to a file. However, after the module under test has completed then stdout messages should be re-redirected to the console.

This routine was tested and is currently used on Windows XP/Pro system.

void SwapIOB(FILE *A, FILE *B) {

    FILE temp;

    // make a copy of IOB A (usually this is "stdout")
    memcpy(&temp, A, sizeof(struct _iobuf));

    // copy IOB B to A's location, now any output
    // sent to A is redirected thru B's IOB.
    memcpy(A, B, sizeof(struct _iobuf));

    // copy A into B, the swap is complete
    memcpy(B, &temp, sizeof(struct _iobuf));

}  // end SwapIOB;

Application code uses SwapIOB() similar to:

FILE *fp;

fp = fopen("X", "w");

SwapIOB(stdout, fp);

printf("text to file X");

SwapIOB(stdout, fp);

fclose(fp);

printf("text to console works, again!");
JackCColeman
  • 3,777
  • 1
  • 15
  • 21