如何在不需要用户输入的情况下刷新标准输入(stdin)?

4
我正在尝试模拟命令行shell。用户输入他们想要执行的shell命令,例如/bin/pwd,代码会执行它。
缓冲区被设置为读取一定数量的字符(比如说20个)。
如果用户输入的字符数超过20个,多余的字符需要在shell循环之前被清除。
我一直在尝试这样做:
int garbageCollector; 
while ((garbageCollector = getchar()) != '\n' && garbageCollector != EOF);

但问题在于getChar()需要您先输入一个字符。

有没有一种不需要用户输入任何内容就可以清空stdin的方法?

while (1) {

    // Prompt user for input
    ssize_t readIn = read(STDIN_FILENO, buffer, BUF_SIZE + 1);
    if (readIn < 0) {
        perror("read() failed");
        return 2;
    } 

    // Null-terminate and remove line return
    buffer[readIn - 1] = '\0'; 

    char program[strlen(buffer)];
    strcpy(program, buffer);
    printf("program is: %s\n", program);

    // Flush stdin
    int garbageCollector; 
    while ((garbageCollector = getchar()) != '\n' && garbageCollector != EOF);

    // Create child process
    child = fork();
    if (child < 0) {
        perror("fork() failed");
        return 3;
    }

    // Start alarm that is triggered after timeout exceeded
    // which then kills child process
    signal(SIGALRM, killChild);
    alarm(timeout); 

    if (child == 0) { // Child
        char* av[] = { program, NULL };
        execve(program, av, NULL);  

    } else {  // Parent
        wait(NULL);
        alarm(0);  // Reset alarm if program executed
    }

    memset(buffer, 0, BUF_SIZE); // Flush buffer
}

2
http://c-faq.com/stdio/stdinflush2.html - Daniel
请注意,20个字符的命令行在中期内不可行 - 它太短了。2000个字符可能更加合适。 - Jonathan Leffler
像Bash这样的Shell将终端置于非规范模式并使用该模式进行读取,而不是使用标准I/O函数。这更类似于使用Curses库。 - Jonathan Leffler
是的,20个字符只是一个例子。我如何在没有标准I/O函数的情况下清空缓冲区? - doctopus
2个回答

2

如果不考虑非POSIX可移植性(请注意,这在Windows上不起作用-但我看到您正在使用fork(),这也无法在Windows上工作),您可以暂时使您尝试刷新的文件描述符为非阻塞,并从中读取所有输入:

最初的回答:

int flush_in(FILE *file)
{
    int ch;
    int flags;
    int fd;

    fd = fileno(file);
    flags = fcntl(fd, F_GETFL, 0);
    if (flags < 0) {
        return -1;
    }
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
        return -1;
    }
    do {
        ch = fgetc(file);
    } while (ch != EOF);
    clearerr(file);
    if (fcntl(fd, F_SETFL, flags)) {
        return -1;
    }
    return 0;
}

然后你需要调用flush_in(stdin)函数。

最初的回答

1

有人在相关问题中建议了这个:

fseek(stdin,0,SEEK_END);

看起来在Mac和Windows上运行良好,但在Linux上不行。对于Linux,Daniel的建议是有效的:

fflush(stdin);

因此,您可以在编译时解决此问题,并根据您编译的操作系统使用fflushfseek
#ifdef defined(_WIN32) || defined(__APPLE__)
  #define flush_stdin(stdin) fseek(stdin,0,SEEK_END)
#elif defined(__linux__) || defined(__unix__)
  #define flush_stdin(stdin) fflush(stdin)
#else
  #define flush_stdin(...) UNIMPLEMENTED

谢谢您。只是一个小问题- 如果我只使用系统调用,即没有像fflush和fseek这样的库函数,那么我该如何实现它? - doctopus
@doctopus 我点赞了Craig的解决方案,因为它更好,并且可以在nix/osx上运行。如果你需要在Windows上执行此操作,请使用fflush,即使传递stdin,它也会完全符合你的要求。 fflushnix/osx上显然是UB,但被广泛实现。 - okovko
当我在底部实现你的代码时,它会使我进入一个无限循环,在那里它不断提示用户输入。@okovko - doctopus
有什么想法可以在不使用库函数的情况下实现这个吗?这让我有点疯狂。 - doctopus
@doctopus 请查看Jonathan Leffler的评论。 - okovko
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接