Основы программирования в Linux
$ <b>./menu2</b>Choice: Please select an actiona — add new recordd — delete recordq — quit<b>q</b>You have chosen: q $ <b>./menu2 > file</b>You are not a terminal! $Как это работает
В новом фрагменте программного кода функция
применяется для проверки связи стандартного вывода с терминалом и прекращения выполнения программы при отсутствии этой связи. Это тот же самый тест, который командная оболочка использует для решения, нужно ли выводить строки приглашения. Возможно и довольно обычно перенаправление иisatty, иstdoutс терминала на другое устройство. Вы можете направить поток ошибок в другой файл:stderr$ <b>./menu2 >file 2>file.error</b>$или объединить оба выводных потока в одном файле:
$ <b>./menu2 >file 2>&1</b>$(Если вы не знакомы с перенаправлением вывода, прочтите еще раз главу 2, в которой мы более подробно рассматриваем синтаксические правила, связанные с ним.) В данном случае вам нужно отправить сообщение непосредственно на терминал пользователя.
Диалог с терминалом
Если нужно защитить части вашей программы, взаимодействующие с пользователем, от перенаправления, но разрешить его для других входных и выходных данных, вы должны отделить общение с пользователем от потоков
иstdout. Это можно сделать, непосредственно считывая данные с терминала и прямо записывая данные на терминал. Поскольку ОС Linux с самого начала создавалась, как многопользовательская система, включающая, как правило, множество терминалов, как непосредственно подсоединенных, так и подключенных по сети, как вы сможете определить тот терминал, который следует использовать?stderrК счастью, Linux и UNIX облегчают жизнь, предоставляя специальное устройство /dev/tty, которое всегда является текущим терминалом или сеансом работы в системе (login session). Поскольку ОС Linux все интерпретирует как файлы, вы можете выполнять обычные файловые операции для чтения с устройства /dev/tty и записи на него.
В упражнении 5.3 вы исправите программу выбора пункта меню так, чтобы можно было передавать параметры в подпрограмму
и благодаря этому лучше управлять выводом. Назовите ее menu3.c.getchoiceУпражнение 5.3. Применение /dev/ttyЗагрузите файл menu2.c и измените программный код так, чтобы входные и выходные данные приходили с устройства /dev/tty и направлялись на это устройство.
#include <stdio.h>#include <unistd.h>#include <stdlib.h>char *menu[] = {"a — add new record", "d — delete record", "q - quit", NULL,};<i>int getchoice(char* greet, char* choices[], FILE* in, FILE* out);</i>int main() {int choice = 0;<i> FILE* input;</i><i> FILE* output;</i>if (!isatty(fileno(stdout))) {<i> fprintf(stderr, "You are not a terminal, OK.\n");</i>}<i> input = fopen("/dev/tty", "r");</i><i> output = fopen("/dev/tty", "w");</i><i> if (!input || !output) {</i><i> fprintf(stderr, "Unable to open /dev/tty\n");</i><i> exit(1);</i><i> }</i>do {<i> choice = getchoice("Please select an action", menu, input, output);</i>printf("You have chosen: %c\n", choice);} while (choice != 'q');exit(0);}<i>int getchoice(char* greet, char *choices[], FILE* in, FILE *out) {</i>int chosen = 0;int selected;char **option;do {<i> fprintf(out, "Choice: %s\n", greet);</i>option = choices;while (*option) {<i> fprintf(out, "%s\n", *option);</i>option++;}<i> do {</i><i> selected = fgetc(in);</i><i> } while(selected == '\n');</i>option = choices;while (*option) {if (selected == *option[0]) {chosen = 1;break;}option++;}if (!chosen) {<i> fprintf(out, "Incorrect choice, select again\n");</i>}} while (!chosen);return selected;}Теперь, когда вы выполните программу с перенаправленным выводом, вы сможете увидеть строки приглашения, а стандартный вывод программы (обозначающий выбранные пункты меню) перенаправляется в файл, который можно просмотреть позже: