2

What is a C equivalent to this C++ answer for temporarily silencing output to cout/cerr and then restoring it?

How to silence and restore stderr/stdout?

(Need this to silence noise from 3rd party library that I am calling, and to restore after the call.)

user2052436
  • 3,917
  • 1
  • 21
  • 38

2 Answers2

3

This is a terrible hack, but should work:

#include <stdio.h>
#include <unistd.h>


int
suppress_stdout(void)
{
    fflush(stdout);
    int fd = dup(STDOUT_FILENO);
    freopen("/dev/null", "w", stdout);
    return fd;
}

void
restore_stdout(int fd)
{
    fflush(stdout);
    dup2(fd, fileno(stdout));
    close(fd);
}

int
main(void)
{
    puts("visible");
    int fd = suppress_stdout();
    puts("this is hidden");
    restore_stdout(fd);
    puts("visible");
}
William Pursell
  • 190,037
  • 45
  • 260
  • 285
  • why it's a hack? Ithink it's confirming to POSIX spec.. question what to do on a non-POSIX system. – Swift - Friday Pie Dec 15 '21 at 23:17
  • 2
    Minor nitpick: Shouldn't it be `close(fd);` last in `restore_stdout`? I made a version of this that works on Posix and Windows [here](https://godbolt.org/z/zWo1vdTvc) - but for some reason it doesn't actually work on Godbolt... – Ted Lyngmo Dec 15 '21 at 23:32
  • Why `STDOUT_FILENO` in `suppress_stdout` while `fileno(stdout)` in `restore_stdout`? – user2052436 Dec 16 '21 at 02:49
  • 1
    @user2052436 No particular reason. I believe either is correct, but there may be pedagogical reasons to prefer one over the other, so I thought I'd demonstrate both. – William Pursell Dec 16 '21 at 13:25
  • 1
    @TedLyngmo Excellent suggestion. Will edit. – William Pursell Dec 16 '21 at 13:26
  • @Swift I call it a "hack" because manipulating the underlying file descriptors of any FILE * feels dirty. I would hate to see this code in production, and would heartily curse its author. – William Pursell Dec 16 '21 at 13:28
  • afaik it happen anyway in various daemons and shell-like programs. As well as magic that hsppens when the program uses fork(). Alot of this information now slips under the hood of frameworks... until programmer runs into an issue where a spawned subprocess had hijacjed a file or socket or closed a stream. If anything, we have to curse Unix DNA. – Swift - Friday Pie Dec 16 '21 at 19:19
  • 1
    @TedLyngmo because permission was denied to open "/dev/null". I added error handling and `perror()` calls to track it (which one always should do). First part works, but stdout forever becomes invalid. Not sure how program can fix that either, because required `freopen ("/dev/stdout", "a", stdout);` also fails - only read the permission exists. I guess their busybox or whatever they use doesn't support that, the trick works in other environments: wandbox, onlinegdb. – Swift - Friday Pie Dec 17 '21 at 07:51
  • @Swift-FridayPie Aha, that makes sense. – Ted Lyngmo Dec 17 '21 at 08:06
2
#include <stdio.h>

#ifdef _WIN32
#define NULL_DEVICE "NUL:"
#define TTY_DEVICE "COM1:"
#else
#define NULL_DEVICE "/dev/null"
#define TTY_DEVICE "/dev/tty"
#endif

int main() {
    printf("hello!\n");

    freopen(NULL_DEVICE, "w", stdout);
    freopen(NULL_DEVICE, "w", stderr);

    printf("you CAN'T see this stdout\n");
    fprintf(stderr, "you CAN'T see this stderr\n");

    freopen(TTY_DEVICE, "w", stdout);
    freopen(TTY_DEVICE, "w", stderr);

    printf("you CAN see this stdout\n");
    fprintf(stderr, "you CAN see this stderr\n");
    return 0;
}
codyne
  • 529
  • 8