Перенаправление stdin и stdout из C - PullRequest
60 голосов
/ 25 февраля 2009

Я хочу снова открыть файловые дескрипторы stdin и stdout (и, возможно, stderr, пока я в нем), чтобы будущие вызовы printf() или putchar() или puts() перешли файл, и будущие вызовы getc() и так далее будут поступать из файла.

1) Я не хочу навсегда потерять стандартный ввод / вывод / ошибку. Я могу захотеть использовать их позже в программе.

2) Я не хочу открывать новые файловые дескрипторы, потому что эти файловые дескрипторы придется либо передавать много, либо глобально (дрожать).

3) Я не хочу использовать open() или fork() или другие зависящие от системы функции, если не могу помочь.

Так в принципе, работает ли это сделать:

stdin = fopen("newin", "r");

И, если это так, как я могу вернуть исходное значение stdin обратно? Нужно ли хранить его в FILE * и просто вернуть его позже?

Ответы [ 8 ]

78 голосов
/ 25 февраля 2009

Зачем использовать freopen()? Спецификация C89 содержит ответ в одном из примечаний к разделу <stdio.h>:

116. Основное использование функции freopen - изменение файла, связанного со стандартом. текстовый поток (stderr, stdin или stdout), так как эти идентификаторы не обязательно должны быть изменяемые значения, до которых значение возвращается функцией fopen может быть назначен.

freopen обычно используется неправильно, например, stdin = freopen("newin", "r", stdin);. Это не более портативный, чем fclose(stdin); stdin = fopen("newin", "r");. Оба выражения пытаются присвоить stdin, что не гарантируется для присвоения.

Правильный способ использования freopen - опустить назначение: freopen("newin", "r", stdin);

15 голосов
/ 25 февраля 2009

Я думаю, вы ищете что-то вроде freopen()

10 голосов
/ 04 июня 2009

Это модифицированная версия метода Тима Поста; Я использовал / dev / tty вместо / dev / stdout. Я не знаю, почему это не работает с stdout (это ссылка на / proc / self / fd / 1):

freopen("log.txt","w",stdout);
...
...
freopen("/dev/tty","w",stdout);

При использовании / dev / tty вывод перенаправляется на терминал, с которого было запущено приложение.

Надеюсь, эта информация полезна.

9 голосов
/ 25 февраля 2009
freopen("/my/newstdin", "r", stdin);
freopen("/my/newstdout", "w", stdout);
freopen("/my/newstderr", "w", stderr);

... do your stuff

freopen("/dev/stdin", "r", stdin);
...
...

Это пиковая стрелка на моем круглом квадратном отверстии-о-метре, чего вы пытаетесь достичь?

Edit:

Помните, что stdin, stdout и stderr являются файловыми дескрипторами 0, 1 и 2 для каждого вновь создаваемого процесса. freopen () должен сохранять те же файлы, просто назначать им новые потоки.

Итак, хороший способ убедиться, что это действительно делает то, что вы хотите, это сделать:

printf("Stdout is descriptor %d\n", fileno(stdout));
freopen("/tmp/newstdout", "w", stdout);
printf("Stdout is now /tmp/newstdout and hopefully still fd %d\n",
   fileno(stdout));
freopen("/dev/stdout", "w", stdout);
printf("Now we put it back, hopefully its still fd %d\n",
   fileno(stdout));

Я полагаю, что это ожидаемое поведение freopen (), как вы можете видеть, вы все еще используете только три файловых дескриптора (и связанные потоки).

Это переопределит любое перенаправление оболочки, так как не будет ничего для перенаправления оболочки. Тем не менее, это, вероятно, собирается сломать трубы. Возможно, вы захотите установить обработчик для SIGPIPE, если ваша программа окажется на блокирующем конце канала (не FIFO, канал).

Итак, ./your_program --stdout /tmp/stdout.txt --stderr /tmp/stderr.txt должен быть легко реализован с помощью freopen () и сохранением тех же фактических файловых дескрипторов. То, что я не понимаю, это то, почему вы должны положить их обратно, когда меняете их? Конечно, если кто-то пропустит любой из этих вариантов, он захочет сохранить его до завершения программы?

9 голосов
/ 25 февраля 2009

Функция os dup2 () должна предоставить то, что вам нужно (если не ссылки на то, что вам нужно).

Точнее говоря, вы можете dup2 () описатель файла stdin в другой дескриптор файла, делать другие вещи с помощью stdin, а затем копировать его обратно, когда захотите.

Функция dup () дублирует дескриптор открытого файла. В частности, он предоставляет альтернативный интерфейс для службы, предоставляемой функцией fcntl (), используя постоянное значение команды F_DUPFD, с 0 в качестве третьего аргумента. Дескриптор дублированного файла разделяет любые блокировки с оригиналом.

В случае успеха dup () возвращает новый дескриптор файла, который имеет следующие общие черты с оригиналом:

  • Тот же открытый файл (или канал)
  • Одинаковый указатель файла (оба файловых дескриптора совместно используют один указатель файла)
  • Тот же режим доступа (чтение, запись или чтение / запись)
3 голосов
/ 21 июля 2012

А пока есть библиотека исходного кода на C, которая сделает все это за вас, перенаправив stdout или stderr. Но самое интересное в том, что он позволяет назначать перехваченным потокам столько функций обратного вызова, сколько вы хотите, что позволяет очень легко отправлять одно сообщение нескольким получателям, БД, текстовому файлу и т. Д.

Кроме того, это упрощает создание новых потоков, которые выглядят и ведут себя так же, как stdout и stderr, где вы также можете перенаправить эти новые потоки в несколько мест.

ищите библиотеку U-Streams C на * oogle.

3 голосов
/ 25 февраля 2009

freopen решает легкую часть. Сохранить старый stdin не сложно, если вы ничего не читали и готовы использовать системные вызовы POSIX, такие как dup или dup2. Если вы начали читать с него, все ставки сняты.

Может быть, вы можете рассказать нам контекст, в котором возникает эта проблема?

Я бы рекомендовал вам придерживаться ситуаций, когда вы готовы отказаться от старых stdin и stdout и поэтому можете использовать freopen.

0 голосов
/ 28 октября 2013

это удобный и удобный способ

freopen("dir","r",stdin);
...