5

Is there a way to to couple two streams (or file descriptors) together so that writing to one stream will also write to the second one? (C, Linux)

Thanks.

jackhab
  • 17,128
  • 37
  • 99
  • 136

5 Answers5

8

Use funopen or fwopen and supply your own write function that writes to multiple FILE*s.

Example:

FILE *files[2] = ...;

FILE *f = fwopen((void *)files, my_writefn);

// ... use f as you like ...

int my_writefn(void *cookie, const char *data, int n) {
  FILE **files = (FILE **)cookie;
  fwrite(data, n, 1, files[0]);
  return fwrite(data, n, 1, files[1]);
}

(Error handling omitted.)

Note that funopen and fwopen are BSD and not in standard Linux. I'm not aware if there's a Linux-compatible equivalent.

laalto
  • 150,114
  • 66
  • 286
  • 303
  • Apparently, no. They are not in man pages (at least on my system). – jackhab Jun 25 '09 at 14:36
  • funopen is on BSD and Mac OS X. On Linux use fopencookie. – mark4o Jun 25 '09 at 15:20
  • @mark4o: Thanks for the info. @Jack: Seems like the implementation using fopencookie() would be quite similar. Not updating my answer here but you should have some pointers to go forward with. – laalto Jun 25 '09 at 15:27
  • This saves computer time. I'm not sure it saves programmer time. Neat trick, though (even if not completely portable, where portable = "among unices"). – Roboprog Jun 25 '09 at 18:29
4

The first thing that came to mind to me was also "tee". So, let's combine C and the shell with popen:

FILE * multi_out;

multi_out = popen( "tee file1.out > file2.out", "w");
/* error checks, actual work here */
pclose( multi_out);
/* error checks here */

As a Unix bigot, I have assumed you are not trying this on Windows.

Roboprog
  • 3,054
  • 2
  • 28
  • 27
  • This way we can write to more than two files also. Just the matter of tricky use of tee. ref. http://www.linuxandlife.com/2013/05/how-to-use-tee-command.html – Jagdish Aug 13 '15 at 15:33
3

User laalto is correct, but on Linux, the function you are looking for is called fopencookie. Correcting laalto's example for Linux results in:

int my_writefn(void *cookie, const char *data, int n) {
  FILE **files = (FILE **)cookie;
  fwrite(data, n, 1, files[0]);
  return fwrite(data, n, 1, files[1]);
}

int noop(void) { return 0; }
cookie_io_functions_t my_fns = {
  (void*) noop,
  (void*) my_writefn,
  (void*) noop,
  (void*) noop
};

FILE *files[2] = ...;

FILE *f = fopencookie((void *)files, "w", my_fns);

// ... use f as you like ...

When you write to f, the system will execute your my_writefn function passing it the data that was passed to fwrite. To make things easier, you may also want to change the buffering for your file stream to be line oriented:

setvbuf(f, NULL, _IOLBF, 0);

That will buffer up the data passed to fwrite until a newline is output or any data is read from any stream attached to the processes (e.g. stdin). NOTE: you must call sevbuf after fopencookie but before any data is written to the stream.

I use line buffering because I usually use fopencookie to redirect stderr to syslog, or over a network socket, and processing line oriented data is easier and more efficient.

Dave Huseby
  • 168
  • 1
  • 5
2

Not sure if it's what you want, but 'tee' in unix does something similar.

Rich Bradshaw
  • 71,795
  • 44
  • 182
  • 241
  • So the "C++" guy gets modded up, but you get modded down for suggesting to "build on the work of others". That's not right... – Roboprog Jun 25 '09 at 18:26
1

You can implement something similar to functionality of tee with boost::iostreams.

stefanB
  • 77,323
  • 27
  • 116
  • 141