首页 > 解决方案 > 如何从 popen 和 fgets 获得无缓冲的输出

问题描述

popen用来执行命令并读取输出。我将文件描述符设置为非阻塞模式,以便我可以设置自己的超时,如下所示:

    auto stream = popen(cmd.c_str(), "r");

    int fd = fileno(stream);
    int flags = fcntl(fd, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(fd, F_SETFL, flags);

    while(!feof(stream)) {
        if(fgets(buffer, MAX_BUF, stream) != NULL) {
            // do something with buffer...
        }
        sleep(10);
    }
    pclose(stream);

这工作得很好,除了fgets一直返回 NULL,直到程序完成执行,此时它按预期返回所有输出。

换句话说,即使程序立即向标准输出输出一些文本和换行符,我的循环也不会立即读取它;它只会在以后看到它。

popen我看到的文档中:

请注意,默认情况下,输出 popen() 流是块缓冲的。

我尝试了一些方法来关闭缓冲(例如setvbuf(stream, NULL, _IONBF, 0)),但到目前为止还没有运气。

如何关闭缓冲以便实时读取输出?

谢谢!

标签: linuxstreampopenfgetsfcntl

解决方案


基于类似 select() 的解决方案会更加准确和灵活。尝试这个 :

    #include <stdio.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/select.h>


    void read_cmd(const char *cmd)
    {
    FILE *stream;
    int fd;
    int flags;
    char buffer[1024];
    fd_set fdset;
    struct timeval timeout;
    int rc;
    int eof;

      stream = popen(cmd, "r");

      fd = fileno(stream);

      eof = 0;
      while(!eof) {

        timeout.tv_sec = 10; // 10 seconds
        timeout.tv_usec = 0;

        FD_ZERO(&fdset);
        FD_SET(fd, &fdset);

        rc = select(fd + 1, &fdset, 0, 0, &timeout);

        switch(rc) {

          case -1: {
            // Error
            if (errno != EINTR) {
              fprintf(stderr, "select(): error '%m' (%d)\n", errno);
            }
            return;
          }
          break;

          case 0: {
            // Timeout
            printf("Timeout\n");
          }
          break;

          case 1: {
            // Something to read
            rc = read(fd, buffer, sizeof(buffer) - 1);
            if (rc > 0) {
              buffer[rc] = '\0';
              printf("%s", buffer);
              fflush(stdout);
            }

            if (rc < 0) {
              fprintf(stderr, "read(): error '%m' (%d)\n", errno);
              eof = 1;
            }

            if (0 == rc) {
              // End of file
              eof = 1;
            }

          }
          break;

        } // End switch

      } // End while

      pclose(stream);

    }

    int main(int ac, char *av[])
    {

      read_cmd(av[1]);

      return 0;

    } // main

推荐阅读