views:

57

answers:

3

Hi there,

I read about freopen to redirect all printf to a file, but I would like the output to be printed on the screen as well. Is there an easy way to redirect the printfs to a file and get the cmd line output?

Thanks!

A: 

From outside the program, use "tee":

# echo foo | tee foo.txt
foo
# cat foo.txt
foo

In fact, you could popen() a channel to a tee that writes the file, though that's system-heavy. Something to this effect:

FILE *stream_to_write_to = popen( "tee filename.txt" );
fprintf( stream_to_write_to, "goes to filename.txt and stdout\n" );

I'm curious to see if there's a from-C quick way of doing this, because at some level, this involves copying data. It's easy to get two filehandles to write to the same place, use dup() or the like, but the opposite is more tricky. It might involve pushing a module (the common example is "connld" onto a stream), though honestly I've never seen this used, so I'd love to see a working code sample myself.

Best reference I can give is "Advanced Programming in the UNIX Environment" by Stevens.

Update:

To speak to R's comment below, the above solution is a slightly heavier version of just fork/exec-ing and redirecting the child's handle someplace else. Both will solve the problem, though I prefer the above because it's easier to clean up, but honestly, both solutions are pretty heavy. Fork() is no lightweight function. If the spirit of the question is to do so without fork/exec, then I'm not sure, I'd love to know, too. If fork/exec is okay, then directly using it or using popen() will hack it.

eruciform
Use `popen` and fork your own child process that mimics `tee` and writes both to the old stdout and the chosen file. Don't exec an external binary; simply implement the `tee`-like functionality in your own program.
R..
@r: you can do that, too, but then you have to handle waiting for the pid and cleaning it up, too, rather than closing the stream. i admit it's totally heavy - so is fork/exec for this. i think the spirit of the question is to copy data without a fork, so this isn't a completely satisfactory answer for me. do you know about stream modules by any chance? that's a cobweb-encrusted corner of unix programming i haven't investigated before. :-)
eruciform
Linux 2.6.17 introduced tee(2) and splice(2), that can be used to implement tee(1) efficiently.
ninjalj
A: 

freopen is not a good idea for redirecting stdout. It will not necessarily reuse the same file descriptor number so child processes may not inherit the new stdout (or may end up with no stdout at all). It's better to use open and then dup2 or close(0) then open to create a new target for stdout.

R..
+1  A: 

Another alternative is to write a function that works like printf, but directs the output to two different places. For instance:

#include <stdio.h>
#include <stdarg.h>

void printf2(FILE *fp, char *format, ...)
{
    va_list ap;
    va_list ap2;

    va_start(ap, format);
    va_copy(ap2, ap);

    vfprintf(fp, format, ap);
    va_end(ap);

    vprintf(format, ap2);
    va_end(ap2);
}

You can then call printf2 the same way you'd call fprintf, and the output will go to both the passed-in FILE pointer and to stdout:

FILE *fp = fopen("/tmp/foo", "w");
printf2(fp, "This is a test.\n");

This approach does not use subprocesses or pipes, and it can be generalized to multiple file pointers, if necessary.

Brian Clapper
+1: nice! i forgot about this. yeah, this is probably the simplest.
eruciform
Hi, thanks for the answer, but in the end I found it easier to use `tee`.
Framester