Подтвердить что ты не робот

Как перенаправить вывод обратно на экран после freopen ( "out.txt", "a", 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?

4b9b3361

Ответ 1

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

Ответ 2

К сожалению, похоже, что нет хорошего способа:

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

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

Ответ 3

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

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

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

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

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

Ответ 4

Используйте fdopen() и dup(), а также 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

Я не уверен, что вы можете надежно выполнять задание в другом месте.

Плохой код, который действительно работает

Код ниже работал на Solaris 10 и MacOS X 10.6.2, но я не уверен, что он надежный. Назначение структуры может работать или не работать с 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;
}

Вы не можете сказать, что вас не предупредили - это игра с огнем!

Если вы находитесь в системе с файловой системой /dev/fd, вы можете создать имя файла, подразумеваемого файловым дескриптором, возвращаемым из dup(), с помощью sprintf(buffer, "/dev/fd/%d", old_stdout), а затем использовать freopen() с этим именем. Это было бы намного надежнее, чем назначение, используемое в этом коде.

Лучшие решения делают код "fprintf (fp,...)" повсюду или используют функцию обложки, которая позволяет вам установить свой собственный указатель файла по умолчанию:

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

Очевидно, вы можете создать mvprintf() и другие функции по мере необходимости.

Пример использования mprintf()

Затем вместо исходного кода вы можете использовать:

#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;
}

(Предупреждение: непроверенный код - слишком высокий уровень уверенности. Кроме того, весь код написан, если вы используете компилятор C99, прежде всего потому, что я объявляю переменные, когда они мне сначала нужны, а не в начале функции.)


Внимание:

Обратите внимание, что если исходная программа вызывается как ./original_program > file или ./original_program | grep something (с перенаправленным выходом) или запускается из задания cron, то открытие /dev/tty обычно не подходит для повторного открытия стандартного вывода потому что исходный стандартный вывод не был терминалом.

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

Ответ 5

В Windows вы можете открыть "CONOUT $".

freopen("test.txt", "w", stdout);
printf("this goes to test.txt");
freopen("CONOUT$", "w", stdout);
printf("this goes to the console\n");

Это, вероятно, не работает, если stdout перенаправляется для начала.

Ответ 6

Следующий код (SwapIOB) используется в Testbenches, который хочет сохранить поток stdout для сравнения с ожидаемым файлом результатов.

Фон. Файловые потоки управляются с использованием структуры _IOB, которая хранится в массиве из 20 записей _IOB. Это включает поток stdout. IOB хранятся в массиве. Когда файл создается, код приложения получает ptr для элемента в этом массиве. Затем код приложения передает этот ptr в ОС для обработки вызовов ввода-вывода. Таким образом, ОС сама НЕ содержит или полагается на свои собственные указатели на приложение IOB.

Требование: при запуске testbench сообщения stdout, выпущенные приложением, должны быть перенаправлены в файл. Однако, после того, как тестируемый модуль завершился, сообщения stdout должны быть перенаправлены на консоль.

Эта процедура была протестирована и в настоящее время используется в системе Windows XP/Pro.

void SwapIOB(FILE *A, FILE *B) {

    FILE temp;

    // make a copy of IOB A (usually this is "stdout")
    memcpy(&temp, A, sizeof(struct _iobuf));

    // copy IOB B to A location, now any output
    // sent to A is redirected thru B IOB.
    memcpy(A, B, sizeof(struct _iobuf));

    // copy A into B, the swap is complete
    memcpy(B, &temp, sizeof(struct _iobuf));

}  // end SwapIOB;

Код приложения использует SwapIOB(), аналогичный:

FILE *fp;

fp = fopen("X", "w");

SwapIOB(stdout, fp);

printf("text to file X");

SwapIOB(stdout, fp);

fclose(fp);

printf("text to console works, again!");

Ответ 7

Нашел этот пост, потому что я столкнулся с проблемой смешивания вывода printf и wprintf в одном потоке (stdout). Изучив этот поток, я обнаружил, что что-то простое, как код ниже, отлично работает, если fclose() не используется. (Протестировано на gcc (Gentoo 4.8.3 p1.1, pie-0.5.9) 4.8.3.). Использование fclose приводит к тому, что более поздние вызовы printf() не работают, только функция fprint сохраняет правильное выполнение задания.

int fDesc; /* file descriptor */
printf("byte-oriented output\n");
/* printf sets the output as byte-oriented */
fDesc = dup(fileno(stdout));
close(stdout);
stdout = fdopen(fDesc,"w");
wprintf(L"wchar-oriented output\n");
/* wprintf sets it to wide-character compat. */
fDesc = dup(fileno(stdout));
close(stdout);
stdout = fdopen(fDesc,"w");
/* reopen for byte-oriented output */
printf("byte-oriented output\n");

Перенаправление вывода в другой файл из оболочки или переходящих труб также отлично работает.

$> a.out > /tmp/out ; cat /tmp/out
$> a.out | sed "s/oriented/aligned/"

Кстати, freopen отлично работает. В коде С++ это будет:

cout << "some cout text here";
stdout=freopen(NULL,"w",stdout); // NULL to reopen same file name!
wcout << L" some wchar_t-s after them"
stdout=freopen(NULL,"w",stdout); // reset the stdout again
cout << " and some couts again." << endl;