Не в состоянии читать входные данные из STDIN / STDIN_FILENO в C? - PullRequest
0 голосов
/ 03 мая 2018

У меня есть аргумент командной строки -

cat file_name | ./a.out 

Проблема не в чтении команды cat внутри программы на C, поскольку мы можем сделать это с помощью read(), fgets(), fgetc(), но реальная проблема, с которой я сталкиваюсь, заключается в чтении данных из cat I Я не могу принять ввод от пользователя, используя fgets.

Вот мой пример кода

while(fgets(buffer, BUFSIZ, stdin ) != NULL )
    puts( buffer ); // Here I have tried strtok( buffer, "\n" ) too.
memset( buffer, 0, BUFSIZ );`

Проблема в том, что после этой строки не запрашивается ввод, как показано ниже:

puts("Name: ");
fgets( buffer, BUFSIZ, stdin );

Помогите мне с тем, что здесь происходит?

Ответы [ 2 ]

0 голосов
/ 03 мая 2018

Если вам нужно использовать stdin для взаимодействия с пользователем, то вам нужно использовать другой файловый дескриптор для чтения входного потока.

Вы можете использовать определенный предварительно открытый дескриптор файла и документ (например, « входной поток должен быть подключен к fd 3 »), но обычный подход заключается в принятии имени файла в качестве команды аргумент Затем вы можете предоставить именованный канал в качестве аргумента; такие оболочки, как Bash, обеспечивают замену процесса , чтобы сделать это проще:

./a.out <(cat file_name)

Когда это выполняется в интерактивном режиме, stdin все еще подключен к терминалу и может использоваться одновременно с потоком из подключенной команды.

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

0 голосов
/ 03 мая 2018

Когда вы делаете cat file_name | ./a.out, стандартный ввод вашей программы привязывается к каналу, связывающему его с выводом cat. Ваша программа никогда не увидит пользовательский ввод - тот самый поток, откуда он прибыл, был заменен вышеупомянутым каналом.

Имейте в виду, я подозреваю, что с какой-то ужасной хитростью, специфичной для POSIX, вы сможете снова открыть ее, перейдя прямо к устройству tty, но это просто плохой дизайн. Если вам нужно как прочитать из файла , так и принять интерактивный ввод пользователя, просто примите файл в качестве аргумента командной строки и используйте stdin для взаимодействия с пользователем.

Редактировать

Это пример специфичных для Unix кладжей, которые можно попробовать, предполагая, что у процесса все еще есть управляющий терминал. После прочтения всего исходного stdin я открываю /dev/tty (который является управляющим терминалом процесса) и повторно связываю stdin с ним.

Отказ от ответственности : это только для развлекательных целей, не делайте этого по-настоящему .

#include <stdio.h>
#include <stdlib.h>

void die(const char *msg) {
    fprintf(stderr, "%s\n", msg);
    fputs(msg, stderr);
    exit(1);
}

int main() {
    /* Read all of stdin and count the bytes read (just to do something with it) */
    int ch;
    unsigned long count = 0;
    while((ch = getchar())!=EOF) {
        count++;
    }
    printf("Read %lu bytes from stdin\n", count);
    /* Open the controlling terminal and re-link it to the relevant C library FILE *
     * Notice that the UNIX fd for stdin is still the old one (it's
     * surprisingly complex to "reset" stdio stdin to a new UNIX fd) */
    if(freopen("/dev/tty", "r", stdin) == NULL) {
        die("Failed freopen");
    }

    /* Do something with this newly gained console */
    puts("How old are you?");
    fflush(stdout);
    int age = -1;
    if(scanf("%d", &age)!=1) {
        die("Bad input");
    }
    printf("You are %d years old\n", age);
    return 0;
}

(ранее у меня было решение, которое проверяло, были ли stderr или stdout по-прежнему консолями, что было еще большим плюсом; спасибо @ rici за напоминание мне о том, что POSIX имеет понятие «управляющий терминал», доступное через /dev/tty)

...