Ответ 1
Я не могу придумать способ сделать это кросс-платформенным способом, но в системах GNU/Linux (и, возможно, других POSIX-совместимых) вы можете freopen ("/dev/tty", "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?
Я не могу придумать способ сделать это кросс-платформенным способом, но в системах 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);
Другое редактирование: если вы используете его для перенаправления вывода из дочернего процесса, как и ваш комментарий в другом месте, вы можете перенаправить его после fork.
Используйте 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,...)" повсюду или используют функцию обложки, которая позволяет вам установить свой собственный указатель файла по умолчанию:
#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);
}
#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() и другие функции по мере необходимости.
Затем вместо исходного кода вы можете использовать:
#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.
Требование: при запуске 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!");
Нашел этот пост, потому что я столкнулся с проблемой смешивания вывода 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;