Как перенаправить вывод назад на экран после freopen (“out.txt”, stdout)

#include <stdio.h>

int main() {
    printf("This goes to screen\n");
    freopen("out.txt", "a", stdout);
    printf("This goes to out.txt");
    freopen("/dev/stdout", "a", stdout);
    printf("This should go to screen too, but doesn't\n");

    return 0;
}

Я называю freopen для перенаправления stdout к out.txt тогда, я печатаю что-то на файле, теперь я хочу перенаправить его назад на экран, но freopen ("/dev/stdout", "a", stdout); не работает. Там какой-либо путь состоит в том, чтобы сделать тот ANSI использования C или системные вызовы POSIX?

29
задан Hoffmann 15 December 2009 в 16:35
поделиться

4 ответа

Я не могу придумать способ сделать это кроссплатформенным способом, но в системах GNU / Linux (и, возможно, в других POSIX-совместимых системах тоже) вы можете freopen ("/ dev / tty", "a", stdout) . Это то, что вы пытались сделать?

22
ответ дан 28 November 2019 в 01:01
поделиться

К сожалению, кажется, что это не лучший способ:

http://c-faq.com/stdio/undofreopen.html

Лучшая рекомендация - не в этом случае используйте freopen.

21
ответ дан 28 November 2019 в 01:01
поделиться

Use fdopen() and dup() as well as freopen().

int old_stdout = dup(1);  // Preserve original file descriptor for stdout.

FILE *fp1 = freopen("out.txt", "w", stdout);  // Open new stdout

...write to stdout...   // Use new stdout

FILE *fp2 = fdopen(old_stdout, "w");   // Open old stdout as a stream

...Now, how to get stdout to refer to fp2?
...Under glibc, I believe you can use:

fclose(stdout);    // Equivalent to fclose(fp1);
stdout = fp2;      // Assign fp2 to stdout
// *stdout = *fp2;   // Works on Solaris and MacOS X, might work elsewhere.

close(old_stdout);   // Close the file descriptor so pipes work sanely

I'm not sure whether you can do the assignment reliably elsewhere.

Dubious code that does actually work

The code below worked on Solaris 10 and MacOS X 10.6.2 - but I'm not confident that it is reliable. The structure assignment may or may not work with Linux glibc.

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

int main(void)
{
    printf("This goes to screen\n");
    int old_stdout = dup(1);  // Consider dup(STDOUT_FILENO) or dup(fileno(stdout))
    FILE *fp1 = freopen("out.txt", "a", stdout);
    printf("This goes to out.txt\n");
    fclose(stdout);
    FILE *fp2 = fdopen(old_stdout, "w");
    *stdout = *fp2;                       // Unreliable!
    printf("This should go to screen too, but doesn't\n");

    return 0;
}

You can't say you weren't warned — this is playing with fire!

If you're on a system with the /dev/fd file system, you could create the name of the file implied by the file descriptor returned from dup() with sprintf(buffer, "/dev/fd/%d", old_stdout) and then use freopen() with that name. This would be a lot more reliable than the assignment used in this code.

The better solutions either make the code use 'fprintf(fp, ...)' everywhere, or use a cover function that allows you set your own default file pointer:

mprintf.c

#include "mprintf.h"
#include <stdarg.h>

static FILE *default_fp = 0;

void set_default_stream(FILE *fp)
{
    default_fp = fp;
}

int mprintf(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    if (default_fp == 0)
        default_fp = stdout;

    int rv = vfprintf(default_fp, fmt, args);

    va_end(args);
    return(rv);
 }

mprintf.h

#ifndef MPRINTF_H_INCLUDED
#define MPRINTF_H_INCLUDED

#include <stdio.h>

extern void set_default_stream(FILE *fp);
extern int  mprintf(const char *fmt, ...);

#endif

Clearly, you can create an mvprintf() and other functions as needed.

Example use of mprintf()

Then, in place of the original code, you can use:

#include "mprintf.h"

int main()
{
    mprintf("This goes to screen\n");
    FILE *fp1 = fopen("out.txt", "w");
    set_default_stream(fp1);
    mprintf("This goes to out.txt\n");
    fclose(fp1);
    set_default_stream(stdout);
    mprintf("This should go to screen too, but doesn't\n");

    return 0;
}

(Warning: untested code - confidence level too high. Also, all code written assuming you use a C99 compiler, primarily because I declare variables when I first need them, not at the beginning of the function.)


Caution:

Note that if the original program is invoked as ./original_program > file or ./original_program | grep something (with redirected output) or is run from a cron job, then opening /dev/tty is not usually appropriate as a way to reopen standard output because the original standard output was not the terminal.

Also, note that if the redirection of standard output is used prior to forking and execing a child program and the original standard output is reinstated in the parent, then the sequence of operations is wrong. You should fork and then adjust the I/O of the child (only), without modifying the parent's I/O at all.

14
ответ дан 28 November 2019 в 01:01
поделиться

Вообще говоря, нельзя. Вы закрыли файл, который мог быть трубкой или чем-то еще. Это не подлежит повторному открытию. Вы могли сохранить значение stdout , затем присвоить ему какое-то значение fopen, а затем закрыть его и скопировать старое значение обратно. Пример:

FILE *o = stdout;
stdout=fopen("/tmp/crap.txt","a");
printf("Oh no!\n");
fclose(stdout);
stdout = o;

Майк Веллер в комментариях ниже предположил, что стандартный вывод не всегда доступен для записи. В этом случае может помочь что-то подобное:

int o = dup(fileno(stdout));
freopen("/tmp/crap.txt","a",stdout);
printf("Oh no!\n");
dup2(o,fileno(stdout));
close(o);

Еще одно изменение: если вы используете его для перенаправления вывода дочернего процесса, как предлагает ваш комментарий в другом месте, вы можете перенаправить его после вилки.

13
ответ дан 28 November 2019 в 01:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: