Перенаправление stdin и stdout с C

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

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

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

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

Итак, в основном, это работает:

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

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


person Chris Lutz    schedule 25.02.2009    source источник
comment
Обратите внимание, что stdin, stdout и stderr - глобальные переменные.   -  person Alexis Wilke    schedule 22.01.2018


Ответы (8)


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

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

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

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

person bk1e    schedule 25.02.2009
comment
@AbiusX Я не уверен, насколько это переносимо, но в среде POSIX вы можете сделать это другим способом - изменив значение базового дескриптора с помощью dup2(): linux.die.net/man/3/fflush. Это позволит вам продублировать дескриптор STDOUT перед его заменой, а затем вы сможете скопировать его обратно. Только не забудьте fflush(). Вот пример использования этого для вилки: stackoverflow.com/questions/9405985/ - person Philip Couling; 07.10.2014

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

person John T    schedule 25.02.2009
comment
Есть ли разница между stdin = freopen (newin, r, stdin); и fclose (stdin); stdin = fopen (newin, r); или нет? - person Chris Lutz; 25.02.2009
comment
fopen открывает файловый объект, в то время как freopen направляет ваш поток данных на файловый объект. Со страницы Эта функция особенно полезна для перенаправления предопределенных потоков, таких как stdin, stdout и stderr, в определенные файлы - person John T; 25.02.2009
comment
Есть ли способ повторно открыть stdin / stdout с их исходными значениями, кроме stdin = freopen (/ dev / tty, r); или что-то? - person Chris Lutz; 25.02.2009
comment
вы используете fclose с потоком, который вы указали на файл. fclose (стандартный вывод); - person John T; 25.02.2009
comment
Уточнение: синтаксическое присвоение stdin, как в stdin = fopen(...) или stdin = freopen(...), не переносимо, потому что stdin может быть макросом. stdio.h может содержать #define stdin __builtin_get_stdin(), и, конечно, вы не можете поместить его в LHS задания. @ChrisLutz, правильный способ использовать freopen - это просто if (freopen("newin", "r", stdin) != NULL) ... --- возвращаемое значение полезно, чтобы указать, не удалось ли открыть, но обычно вам не нужно назначать его чему-либо. - person Quuxplusone; 19.12.2013

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

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

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

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

person Community    schedule 03.06.2009

Функция ОС dup2 () должна предоставить то, что вам нужно < / strong> (если не ссылки именно на то, что вам нужно).

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

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

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

  • Тот же открытый файл (или труба)
  • Один и тот же файловый указатель (оба файловых дескриптора используют один файловый указатель)
  • Одинаковый режим доступа (чтение, запись или чтение / запись)
person gahooa    schedule 25.02.2009
comment
Однако в вопросе прямо говорится, что нельзя использовать системные функции. - person unwind; 25.02.2009
comment
И вопрос явно говорит об ограничении количества открытых потоков / файловых дескрипторов. - person Tim Post♦; 25.02.2009

freopen("/my/newstdin", "r", stdin);
freopen("/my/newstdout", "w", stdout);
freopen("/my/newstderr", "w", stderr);

... do your stuff

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

Стрелка на моем измерителе с круглым штифтом, квадратным отверстием достигает пика, чего вы пытаетесь достичь?

Изменить:

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

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

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, pipe).

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

person Tim Post♦    schedule 25.02.2009
comment
Я пытаюсь добавить в свою программу способ перенаправления собственного стандартного ввода и вывода для людей, которым не нравится перенаправление их оболочки. - person Chris Lutz; 25.02.2009
comment
Да, в этом вам поможет freopen (). - person Tim Post♦; 26.02.2009
comment
Это не работает так, как вы это описываете. Я использовал следующий код: printf (Это должно быть видно на экране! \ N); freopen (testop.txt, w, stdout); printf (Это должно быть в тесте! \ n); fflush (стандартный вывод); fclose (стандартный вывод); freopen (/ dev / stdout, w, stdout); printf (И это должно снова появиться на экране! \ n); - person VectorVictor; 26.05.2016
comment
@VectorVictor Odd, я снова запущу код, когда выйду с работы (я уверен, что тестировал его, когда размещал это еще в 2009 году). Я снова оцениваю с помощью современного компилятора. - person Tim Post♦; 26.05.2016
comment
Привет, Тим. Возникли проблемы с вводом комментария в моем последнем сообщении. Он почему-то не любит copy'n'paste. Хотел продолжить, чтобы перечислить вывод и предоставить подробную информацию о системе, но оно было отправлено до того, как я был готов. Первые две инструкции действуют так, как и следовало ожидать, однако последняя. И это снова не должно отображаться на экране. Я компилирую XCode clang 500.2.79 в Mac OS X 10.9.5. Я даже попытался повторно открыть stdout в / dev / tty, тоже без особой радости. Как будто после переопределения stdout игра окончена. - person VectorVictor; 29.05.2016

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

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

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

person Norman Ramsey    schedule 25.02.2009
comment
В контексте, я думаю, что мог бы отказаться от старого stdin / stdout, если бы мне действительно было нужно. Я пытаюсь добавить в свою программу возможность перенаправления самой программы на stdin и stdout для людей, которые запускают программу и не очень хорошо знают свою оболочку. Просто черт возьми. - person Chris Lutz; 25.02.2009
comment
У моих учеников определенно есть проблемы с перенаправлением ввода-вывода, так что ваша цель кажется хорошей. Может быть, вам лучше использовать параметры командной строки -i и -o для именования файлов ввода и вывода? - person Norman Ramsey; 25.02.2009
comment
Это мой план более или менее (-i и -o уже приняты, но это общий интерфейс), мне просто нужен был способ его реализовать (без необходимости менять все мои printf () на fprintf () и передавать две файловые ручки в разные стороны). Кроме того, было бы неплохо установить флаг для перенаправления stderr. - person Chris Lutz; 25.02.2009
comment
В конечном итоге вы будете счастливы, передавая дескрипторы файлов, но если у вас есть устаревший код, freopen () - ваш друг. - person Norman Ramsey; 25.02.2009

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

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

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

person john    schedule 21.07.2012

Это наиболее доступный, удобный и полезный способ сделать

freopen("dir","r",stdin);
person Community    schedule 28.10.2013