Reputation: 1775
This is a follow-up to How to capture output of printf? - specifically the answer by jxh.
I am having the same trouble with the code in the answer that Viesturs was having in 2018. It was suggested to him that he open a new question, which he said his account didn't allow, so I'm opening a new question along the same lines.
On that answer, in 2012, the original questioner said it worked. In 2018, Viesturs said after the code was ran, further output neither appeared on the console nor in the redirected file.
I'm having the same problem. Using the code below, this shouldn't be redirected\n
isn't shown on the console, and isn't in the redirected file. The redirected file only contains the first printed line.
I'm using gcc 8.3.0, and also tried gcc 6.5.0. glibc 2.29.
Note that I do NOT want to freopen
with "CON"
afterward. Even if that works, I want to preserve the original stdout, since it could itself be a redirect. (Although in my testing, it's just been the console.)
Either: the jxh answer has a bug in it; I'm making the same mistake Viesturs was; or (my best guess) there was a bug introduced (or fixed) sometime earlier than gcc 6.5.0 or glibc 2.29.
redirected.c
#include <unistd.h>
#include <stdio.h>
void funcB() {
printf("this should be redirected\n");
fflush(stdout);
}
int main() {
int stdout_fd = dup(STDOUT_FILENO);
freopen("/tmp/redirected", "w", stdout);
funcB();
fclose(stdout);
dup2(stdout_fd, STDOUT_FILENO);
stdout = fdopen(STDOUT_FILENO, "w");
close(stdout_fd);
printf("this shouldn't be redirected\n");
fflush(stdout); // shouldn't make a difference if this is here
}
Output:
$ gcc redirected.c
$ ./a.out
<THERE IS NO OUTPUT>
$ cat /tmp/redirected
this should be redirected
Upvotes: 1
Views: 969
Reputation: 180998
It is not guaranteed that there is any way to reconnect stdin
to the original file after reassigning it via freopen()
. In particular, it is not specified whether the stdin
macro even expands to a modifiable lvalue, much less what effect assigning to the result has. That's in fact the reason for freopen()
in the first place.
The method you are attempting to use is plausible, but again, not guaranteed to work, and I observe the same behavior you describe myself. Probably your version of printf
and mine have optimized in the identity of stdout
, so that assigning a new value to that symbol does not affect that function's output destination, which you are closing prior to printing.
You are using POSIX interfaces (dup
and dup2
), and on a POSIX-conforming implementation, the most likely approach to work is to avoid closing stdin
, but instead redefine the file to which it writes, via dup2()
only. You should be careful in this case to fflush()
before such a swap, something like this:
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void funcB() {
printf("this should be redirected\n");
fflush(stdout);
}
int main() {
int stdout_fd = dup(STDOUT_FILENO);
int rd_fileno = open("/tmp/redirected", O_WRONLY | O_CREAT | O_TRUNC, 0600);
dup2(rd_fileno, STDOUT_FILENO);
funcB();
fflush(stdout);
dup2(stdout_fd, STDOUT_FILENO);
close(stdout_fd);
printf("this shouldn't be redirected\n");
fflush(stdout); // shouldn't make a difference if this is here
}
That version does work for me, and it rests on the same foundation as redirection generally does in POSIX. Note in particular that stdout
is never closed (except automatically when the program terminates).
Upvotes: 2
Reputation: 215397
No correct solution to this can involve:
stdout
, because stdout
is not a variable/lvalue. It's a macro that expands to an expression of type FILE *
. If it happens to also be an lvalue, that's an implementation detail for whatever particular implementation it happens on, not part of the language.fclose(stdout)
, because after fclose(stdout)
, any further use of stdout
anywhere in the program, implicit (e.g. printf
) or explicit, for the rest of the duration of execution, invokes undefined behavior.A good solution does not involve freopen
either, although you might be able to make that work. Instead, simply use dup2
, with fflush
. This answer (by me) explains how to do it:
https://stackoverflow.com/a/4832902/379897
Substitute the file you want to direct to in place of "/dev/null"
.
Upvotes: 3