Используйте 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
обычно не подходит для повторного открытия стандартного вывода потому что исходный стандартный вывод не был терминалом.
Также обратите внимание, что если перенаправление стандартного вывода используется перед разветвлением и выполнением дочерней программы, а исходный стандартный вывод восстанавливается в родительском, то последовательность операций неправильна. Вы должны форкнуть, а затем настроить ввод / вывод ребенка (только), не изменяя ввод / вывод родителя вообще.