jik@athena.mit.edu (Jonathan I. Kamens) (03/15/91)
In article <574@dprmpt.UUCP>, larry@dprmpt.UUCP (Larry) writes: |> In a C program, how can I change stderr from wherever it is directed |> via the command line to a file, then later revert it back to it's |> original destination? This is not a comp.unix.wizards question. I have cross-posted this response to comp.unix.questions and directed followups there. First, "stderr_fd = dup(fileno(stderr))" to preserve the original stderr output direction. Then "fclose(stderr)", and immediately do "stderr = fopen(your_stderr_file, "w")" (the "immediately" is so that the newly opened file will get the same file descriptor stderr had, in case you've got that file descriptor hard-coded somewhere in your code). When you're done, "fclose(stderr)" and do "stderr = fdopen(stderr_fd, "w")" to put things back the way they were originally. -- Jonathan Kamens USnail: MIT Project Athena 11 Ashford Terrace jik@Athena.MIT.EDU Allston, MA 02134 Office: 617-253-8085 Home: 617-782-0710
jik@athena.mit.edu (Jonathan I. Kamens) (03/15/91)
Someone has pointed out to me in E-mail that assigning to stderr as I indicated in my previous message won't work on many systems because of the way stderr is defined. There are ways to get around this. One of them, a rather gross method that will nonetheless work on many systems, is to assign to stderr like this: *(stderr) = *(value to assign) e.g. *(stderr) = *(fopen(error_file, "w")); However, this won't work if FILE * is an opaque type whose contents are not spelled out for the compiler in <stdio.h>. It is possible to replace stderr with a file doing something like this: freopen(error_file, "w", stderr); However, if you do things this way, I can't see any way to put the original stderr back in place -- there is no "fdreopen". That's unfortunate. -- Jonathan Kamens USnail: MIT Project Athena 11 Ashford Terrace jik@Athena.MIT.EDU Allston, MA 02134 Office: 617-253-8085 Home: 617-782-0710
jik@athena.mit.edu (Jonathan I. Kamens) (03/15/91)
Another thing to mention about this question occurred to me in the shower this morning (really! :-). Why do you need to redirect stderr temporarily? It seems to me that there are two possible explanations for this: 1) You want the error messages your program prints to go somewhere else temporarily. 2) You want to capture the error messages printed by a process invoked by your program. If the reason is (1), then a cleaner way to do what you're trying to do would be to keep a global FILE * variable in your namespace to which your program prints errors, and then change that variable when you want to redirect errors elsewhere. In other words, the routine that replaces stderr with a file would do something like this (you add the error checking): extern FILE *error_file; extern char *error_file_name; error_file = fopen(error_file_name, "w"); and the routing that puts stderr back would simply do this: error_file = stderr; and then all of your routines that print errors would print to error_file instead of printed to stderr explicitly. If the reason is (2), then once again there is a cleaner way to do this. Obviously, you have to fork in order to run a new process if you expect control to return to your process (and if you don't expect control to return to your process, there's no reason for you to have to put stderr back, so the question is moot). After the fork, simply freopen the error file on top of stderr in the child that runs the subprocess. The parent will get to keep the original stderr, and the child's errors will go to the file. If you're using popen() or system() or something right now, then you'll either have to write your own version of popen() or system() that takes a file name to put errors in and does the freopen after the fork, or you can put a wrapper around the standard functions to be a bit clever about doing things so that you don't have to roll your own. -- Jonathan Kamens USnail: MIT Project Athena 11 Ashford Terrace jik@Athena.MIT.EDU Allston, MA 02134 Office: 617-253-8085 Home: 617-782-0710
tb@sequent.sequent.com (Tony Booker) (03/15/91)
In article <1991Mar14.225058.13507@athena.mit.edu> jik@athena.mit.edu (Jonathan I. Kamens) writes:
It is possible to replace stderr with a file doing something like this:
freopen(error_file, "w", stderr);
However, if you do things this way, I can't see any way to put the
original stderr back in place -- there is no "fdreopen". That's
unfortunate.
I beleive the return value of freopen() is the original file for you
to salt away as necessary.
--
The above drivel is mine. Company drivel sounds _MUCH_ more plausible ;^)
jik@athena.mit.edu (Jonathan I. Kamens) (03/18/91)
In article <TB.91Mar15072648@sequent.sequent.com>, tb@sequent.sequent.com (Tony Booker) writes: |> In article <1991Mar14.225058.13507@athena.mit.edu> jik@athena.mit.edu (Jonathan I. Kamens) writes: |> However, if you do things this way, I can't see any way to put the |> original stderr back in place -- there is no "fdreopen". That's |> unfortunate. |> |> I beleive the return value of freopen() is the original file for you |> to salt away as necessary. From freopen(3): Freopen substitutes the named file in place of the open stream. It returns the original value of stream. The ori- ginal stream is closed. In other words, yes, it returns the original stream, but it's closed and replaced by the new file, so you can't do anything useful with it. To verify this, compile and run the following program. If what you say works, then the second fprintf should cause output to go to the tty; if not, the second fprintf should appear in the testfile. On my system, it does. #include <stdio.h> #define TESTFILE "/tmp/freopen-testfile" main() { FILE *old; old = freopen(TESTFILE, "w", stdout); fprintf(stdout, "This is being written to stdout, now %s.\n", TESTFILE); fprintf(old, "This is being written to the old stdout.\n"); fclose(stdout); fclose(old); exit(0); } -- Jonathan Kamens USnail: MIT Project Athena 11 Ashford Terrace jik@Athena.MIT.EDU Allston, MA 02134 Office: 617-253-8085 Home: 617-782-0710 P.S. It's usually a good idea to actually try suggestions like this out before posting them to the net. Yes, I know, I'm not one to talk, considering that I posted about assigning to stderr when that won't work on most systems anyway. But I thought I'd point it out anyway. :-)