Как перенаправить вывод обратно на экран после 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?
7 ответов
Я не могу придумать способ сделать это кроссплатформенным образом, но в системах GNU/Linux (и, возможно, других POSIX-совместимых тоже) вы можете freopen ("/dev/tty", "a", stdout)
, Это то, что вы пытались сделать?
К сожалению, кажется, что нет хорошего способа:
http://c-faq.com/stdio/undofreopen.html
Лучшая рекомендация - не использовать freopen в этих условиях.
Вообще говоря, вы не можете. Вы закрыли файл, который мог быть трубой или чем-то еще. Это не открывается. Вы могли бы сохранить 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);
Другое редактирование: если вы используете его для перенаправления вывода из дочернего процесса, как подсказывает ваш комментарий в другом месте, вы можете перенаправить его после разветвления.
Использование 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,...), либо используют функцию cover, которая позволяет вам установить собственный указатель на файл по умолчанию:
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
обычно не подходит для повторного открытия стандартного вывода, потому что исходный стандартный вывод не был терминалом.
Также обратите внимание, что если перенаправление стандартного вывода используется перед разветвлением и удалением дочерней программы, а исходный стандартный вывод восстанавливается в родительском, то последовательность операций является неправильной. Вы должны разветвить и затем настроить ввод / вывод дочернего элемента (только), не изменяя ввод / вывод родителя вообще.
В 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 перенаправлен для начала.
Следующий код (SwapIOB) используется в Testbenches, которые хотят сохранить поток stdout для сравнения с файлом ожидаемых результатов.
Справочная информация: Управление файловыми потоками осуществляется с помощью структуры _IOB, которая хранится в массиве из 20 записей _IOB. Это включает в себя поток stdout. IOB хранятся в массиве. Когда файл создан, код приложения получает ptr для элемента в этом массиве. Затем код приложения передает этот ptr в ОС для обработки вызовов ввода / вывода. Таким образом, ОС НЕ содержит и не полагается на свои собственные указатели на IOB приложения.
Требование: при запуске тестового стенда сообщения стандартного вывода, создаваемые приложением, должны перенаправляться в файл. Однако после завершения тестируемого модуля сообщения 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's location, now any output
// sent to A is redirected thru B's 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!");
Нашел этот пост, потому что столкнулся с проблемой смешивания вывода 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 тоже отлично работает. В коде C++ это будет:
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;