在 C 中同时通过管道传输标准输入和键盘

piped stdin and keyboard same time in C

我也阅读了之前关于这个问题的问题。 fflush(stdin) 在这种情况下对我不起作用。 我希望我的程序从管道标准输入读取并从中间的键盘输入继续。

int main()
{
    int userin = 3;
    read_input();   
    userin = print_menu();
    userin = parse_input(userin);

    return 0;
}  

我想从一个文件中读取数据,该文件作为 pipied stding 传递给程序,例如

节目
int read_input(){
    char line[200];
    char word[MAX_STRING+1];
    int line_number = 0;

    while(fgets(line, sizeof(line), stdin) != NULL ){
        //do something
        printf("%s",line); 
        line_number++;
    }
}

现在 read_input 必须完成从管道输入的读取。 'print_menu' 必须继续从键盘读取。

int print_menu()
{
    int userinput;
    char c;
    char num[4];
    while((c=getchar()) != '\n' && c != EOF && c != '\r');

    printf("\n1. Choice 1 \n");
    printf("2. Choice 2\n");
    printf("3. Exit\n");
    printf("Enter your choice (1-3): ");

   /* scanf("%d", &userinput); */
   /* fgets(num,80,stdin);  */

   scanf("%s", num);
   userinput = atoi(num);   
   return userinput;
}

int parse_input(int userinput)
{
    char num[4];
    while( userinput > 3 || userinput < 1 ){
       printf("Sorry, that is not a valid option\n");
       printf("Enter your choice (1-3): ");
       scanf("%s", num);
       userinput = atoi(num);
    
       /* scanf("%d", &userinput); */
       /* while( (c = getchar()) == '\n'); */
    }
     return userinput;
}

我的输出是

的无限循环
Enter your choice (1-3): Sorry, that is not a valid option

当我删除 read_input 方法时,管道标准输入程序工作正常。 我不知道解决这个问题,有人有想法吗..

如果您希望您的程序接受键盘输入,请不要重定向其标准输入。如果您重定向程序的标准输入,不要指望能够通过键盘输入数据。

如果您的程序需要同时接受来自文件和键盘的输入,请将文件名作为参数提供给它,然后让它打开并读取该文件。

如果您必须处理某种不可修改的库函数,需要通过 stdin 提供输入,您希望该输入来自文件, 你想在别处接受键盘输入,那么你也许可以玩 dup2()ing 文件描述符的游戏来让它工作。一旦你开始工作,考虑解雇负责该库函数的白痴。

管道(由 shell 控制)通常在文件描述符 0 上,这是标准输入。你可以使用 dup2() to "move" that to a different file descriptor, and use freopen() to continue reading from that. You can also open the /dev/tty device (or whatever the tty program knows about) 并使 that 你的标准输入。

dialog program does this for the --gauge (progress bar) option. The reason why it does this is because dialog is a curses application (which by default uses the standard input and output). The actual pipe file-descriptor is as well optional, so quoting from its manual page给出了一些提示:

--input-fd fd

Read keyboard input from the given file descriptor. Most dialog scripts read from the standard input, but the gauge widget reads a pipe (which is always standard input). Some configurations do not work properly when dialog tries to reopen the terminal. Use this option (with appropriate juggling of file-descriptors) if your script must work in that type of environment.

实现此功能的逻辑在 util.c 中,来自 init_dialog 函数。这是其中的主要部分,以了解如何使用这些函数:

    dialog_state.pipe_input = stdin;
    if (fileno(input) != fileno(stdin)) {
        if ((fd1 = dup(fileno(input))) >= 0
            && (fd2 = dup(fileno(stdin))) >= 0) {
            (void) dup2(fileno(input), fileno(stdin));
            dialog_state.pipe_input = fdopen(fd2, "r");
            if (fileno(stdin) != 0)     /* some functions may read fd #0 */
                (void) dup2(fileno(stdin), 0);
        } else {
            dlg_exiterr("cannot open tty-input");
        }
        close(fd1);
    } else if (!isatty(fileno(stdin))) {
        if ((fd1 = open_terminal(&device, O_RDONLY)) >= 0) {
            if ((fd2 = dup(fileno(stdin))) >= 0) {
                dialog_state.pipe_input = fdopen(fd2, "r");
                if (freopen(device, "r", stdin) == 0)
                    dlg_exiterr("cannot open tty-input");
                if (fileno(stdin) != 0)         /* some functions may read fd #0 */
                    (void) dup2(fileno(stdin), 0);
            }
            close(fd1);
        }
        free(device);
    }

我写的一个程序几乎与我从 stdin 读取二进制数据(从其他程序的 stdout 管道输入)的程序有相同的情况,但我想添加键盘命令(通过raw terminal support)。显然,同时读取管道数据和键盘会导致一些奇怪的结果。

这是我的代码,可以运行并经过测试:

FILE * fp;
int fd;

fd = dup(fileno(stdin));
fp = fdopen(fd, "r");
(void) freopen("/dev/tty", "r", stdin);

现在,我的程序从 fp(一个 FILE* 对象)读取其二进制数据,同时读取 stdin(现在与 "/dev/tty" 相关联)以获得击键。

请注意,我丢弃了 freopenFILE* 对象)中的 return 值,因为如果需要,我可以通过 stdin 引用它。我的经验是不需要 close()fclose() 标准文本流。我fclose(fp)读取二进制数据结束。