77

I want to reopen the stdin and stdout (and perhaps stderr while I'm at it) filehandles, so that future calls to printf() or putchar() or puts() will go to a file, and future calls to getc() and such will come from a file.

1) I don't want to permanently lose standard input/output/error. I may want to reuse them later in the program.

2) I don't want to open new filehandles because these filehandles would have to be either passed around a lot or global (shudder).

3) I don't want to use any open() or fork() or other system-dependent functions if I can't help it.

So basically, does it work to do this:

stdin = fopen("newin", "r");

And, if it does, how can I get the original value of stdin back? Do I have to store it in a FILE * and just get it back later?

Chris Lutz
  • 73,191
  • 16
  • 130
  • 183

8 Answers8

97

Why use freopen()? The C89 specification has the answer in one of the endnotes for the section on <stdio.h>:

116. The primary use of the freopen function is to change the file associated with a standard text stream (stderr, stdin, or stdout), as those identifiers need not be modifiable lvalues to which the value returned by the fopen function may be assigned.

freopen is commonly misused, e.g. stdin = freopen("newin", "r", stdin);. This is no more portable than fclose(stdin); stdin = fopen("newin", "r");. Both expressions attempt to assign to stdin, which is not guaranteed to be assignable.

The right way to use freopen is to omit the assignment: freopen("newin", "r", stdin);

bk1e
  • 23,871
  • 6
  • 54
  • 65
  • 2
    @AbiusX I'm not sure how portable this is but under POSIX environments you can do this another way - by changing the meaning of the underlying handle with `dup2()`: http://linux.die.net/man/3/fflush. This would let you duplicate the STDOUT handle before replacing it, then you can copy back. Just remember to `fflush()`. There's an example of this being used to fork here: http://stackoverflow.com/questions/9405985/linux-3-0-executing-child-process-with-piped-stdin-stdout – Philip Couling Oct 07 '14 at 09:40
  • For those who wants to close standard descriptor before reopen it, there is a solution tested in msvc2015 UCRT, use sequence of `fclose`+`_close`+`freopen`: `const int stdin_fileno = _fileno(stdin); fclose(stdin); if (stdin_fileno < 0) _close(STDIN_FILENO); /* may reallocate console here */; freopen("CONIN$", "r", stdin); /* use _get_osfhandle + SetStdHandle for not console application */`. This has sence because `freopen` does not reuse already allocated descriptor and allocates a new one. So to force it to reuse the descriptor you have to close it first. – Andry Aug 01 '21 at 09:10
  • And how do you do when you already have a FILE* that comes from a pipe and that doesn't have a path on the filesystem? – v.oddou May 11 '23 at 16:01
17

I think you're looking for something like freopen()

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
John T
  • 23,735
  • 11
  • 56
  • 82
  • Is there any difference between stdin = freopen("newin", "r", stdin); and fclose(stdin); stdin = fopen("newin", "r"); or no? – Chris Lutz Feb 25 '09 at 06:02
  • fopen opens a file object while freopen points your data stream at the file object.From the page "This function is specially useful for redirecting predefined streams like stdin, stdout and stderr to specific files" – John T Feb 25 '09 at 06:11
  • 1
    Is there any way to reopen stdin/stdout to their original values other than stdin = freopen("/dev/tty", "r"); or something? – Chris Lutz Feb 25 '09 at 06:25
  • 1
    you use fclose with the stream you pointed at the file. fclose(stdout); – John T Feb 25 '09 at 06:32
  • 1
    Clarification: Syntactically assigning to `stdin`, as in `stdin = fopen(...)` or `stdin = freopen(...)`, is not portable, because `stdin` is allowed to be a macro. stdio.h might contain `#define stdin __builtin_get_stdin()`, and of course you can't put that on the LHS of an assignment. @ChrisLutz, the right way to use `freopen` is simply `if (freopen("newin", "r", stdin) != NULL) ...` --- the return value is useful to indicate whether the open failed, but you shouldn't generally need to assign it to anything. – Quuxplusone Dec 19 '13 at 18:06
  • @Quuxplusone but for freopen to be usable you need a filename (a path). When you have a pipe you do what? – v.oddou May 11 '23 at 16:02
  • @v.oddou: Googling "fdreopen" turns up [Oracle's answer to that problem](https://github.com/oracle/dtrace-utils/blob/f543fa6706c0b31364356c01bf3de63e3cce8ad1/libdtrace/dt_printf.c#L1615-L1622): "freopen'ing `/dev/fd/[fileno]`." I guess that's probably still the state of the art. – Quuxplusone May 11 '23 at 19:14
  • @Quuxplusone I know the everything as a file Unix philosophy, but sorry I didn't precise it was windows that gives me problems. I tried `dup2` and it doesn't work (errno 9). – v.oddou May 12 '23 at 02:18
11

This is a modified version of Tim Post's method; I used /dev/tty instead of /dev/stdout. I don't know why it doesn't work with stdout (which is a link to /proc/self/fd/1):

freopen("log.txt","w",stdout);
...
...
freopen("/dev/tty","w",stdout);

By using /dev/tty the output is redirected to the terminal from where the app was launched.

Hope this info is useful.

Michael Myers
  • 188,989
  • 46
  • 291
  • 292
10
freopen("/my/newstdin", "r", stdin);
freopen("/my/newstdout", "w", stdout);
freopen("/my/newstderr", "w", stderr);

... do your stuff

freopen("/dev/stdin", "r", stdin);
...
...

This peaks the needle on my round-peg-square-hole-o-meter, what are you trying to accomplish?

Edit:

Remember that stdin, stdout and stderr are file descriptors 0, 1 and 2 for every newly created process. freopen() should keep the same fd's, just assign new streams to them.

So, a good way to ensure that this is actually doing what you want it to do would be:

printf("Stdout is descriptor %d\n", fileno(stdout));
freopen("/tmp/newstdout", "w", stdout);
printf("Stdout is now /tmp/newstdout and hopefully still fd %d\n",
   fileno(stdout));
freopen("/dev/stdout", "w", stdout);
printf("Now we put it back, hopefully its still fd %d\n",
   fileno(stdout));

I believe this is the expected behavior of freopen(), as you can see, you're still only using three file descriptors (and associated streams).

This would override any shell redirection, as there would be nothing for the shell to redirect. However, its probably going to break pipes. You might want to be sure to set up a handler for SIGPIPE, in case your program finds itself on the blocking end of a pipe (not FIFO, pipe).

So, ./your_program --stdout /tmp/stdout.txt --stderr /tmp/stderr.txt should be easily accomplished with freopen() and keeping the same actual file descriptors. What I don't understand is why you'd need to put them back once changing them? Surely, if someone passed either option, they would want it to persist until the program terminated?

Tim Post
  • 33,371
  • 15
  • 110
  • 174
  • I'm trying to add a way for my program to redirect it's own standard input and output, for people who aren't comfortable with their shell's redirection. – Chris Lutz Feb 25 '09 at 17:43
  • Yes, freopen() is going to be your friend for that. – Tim Post Feb 26 '09 at 03:13
  • This does not work as you describe it. I used the following code: printf ("This should be visible on the screen!\n"); freopen ("testop.txt", "w", stdout); printf ("This should go to testop!\n"); fflush (stdout); fclose (stdout); freopen ("/dev/stdout", "w", stdout); printf ("And this should go to the screen again!\n"); – VectorVictor May 26 '16 at 08:32
  • @VectorVictor Odd, I'll run the code again when I get off from work (I'm sure I tested it when I posted this, way back in 2009). I'll evaluate again with a modern compiler. – Tim Post May 26 '16 at 12:13
  • Hi Tim. Had trouble with the comment input in my last post. It doesn't like copy'n'paste for some reason. Wanted to go on to list the output and give sys details but it got sent before I was ready. The first two instructions do as one would expect, however, the final one "And this should go to the screen again" doesn't. I'm compiling on XCode clang 500.2.79 on Mac OS X 10.9.5. I even tried re-opening stdout at /dev/tty also with no joy. It's as if once stdout has been redefined, it's game over. – VectorVictor May 29 '16 at 12:55
9

The os function dup2() should provide what you need (if not references to exactly what you need).

More specifically, you can dup2() the stdin file descriptor to another file descriptor, do other stuff with stdin, and then copy it back when you want.

The dup() function duplicates an open file descriptor. Specifically, it provides an alternate interface to the service provided by the fcntl() function using the F_DUPFD constant command value, with 0 for its third argument. The duplicated file descriptor shares any locks with the original.

On success, dup() returns a new file descriptor that has the following in common with the original:

  • Same open file (or pipe)
  • Same file pointer (both file descriptors share one file pointer)
  • Same access mode (read, write, or read/write)
Community
  • 1
  • 1
gahooa
  • 131,293
  • 12
  • 98
  • 101
3

freopen solves the easy part. Keeping old stdin around is not hard if you haven't read anything and if you're willing to use POSIX system calls like dup or dup2. If you're started to read from it, all bets are off.

Maybe you can tell us the context in which this problem occurs?

I'd encourage you to stick to situations where you're willing to abandon old stdin and stdout and can therefore use freopen.

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
  • In context, I think I could get away with abandoning the old stdin/stdout if I really had to. I'm trying to add to my program an option for the program to redirect stdin and stdout itself, for people who are running the program and don't know their shell very well. Just for the hell of it. – Chris Lutz Feb 25 '09 at 06:40
  • 1
    My students definitely have trouble with I/O redirection, so your goal sound like a good one. Maybe you'd be better off with command-line options -i and -o to name input and output files? – Norman Ramsey Feb 25 '09 at 06:43
  • That's my plan more or less (-i and -o are already taken, but that's the general interface), I just needed a way to implement it (without having to change all my printf()s to fprintf()s and pass around two filehandles every which way). Plus, a flag for stderr rerouting would be nice. – Chris Lutz Feb 25 '09 at 06:47
  • 1
    In the long run you'll be happy passing filehandles, but if you have legacy code in place, freopen() is your friend. – Norman Ramsey Feb 25 '09 at 06:48
2

And in the meantime, there's a C source code library that will do all this for you, redirecting stdout or stderr. But the cool part is that it lets you assign as many callback functions as you want to the intercepted streams, allowing you then to very easily send a single message to multiple destinations, a DB, a text file, etc.

On top of that, it makes it trivial to create new streams that look and behave the same as stdout and stderr, where you can redirect these new streams to multiple locations as well.

look for U-Streams C library on *oogle.

john
  • 29
  • 1
0

This is the most readily available, handy and useful way to do

freopen("dir","r",stdin);
Ojasv singh
  • 474
  • 8
  • 19