Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/25.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Linux 如何从popen&;获得无缓冲输出;fgets_Linux_Stream_Popen_Fgets_Fcntl - Fatal编程技术网

Linux 如何从popen&;获得无缓冲输出;fgets

Linux 如何从popen&;获得无缓冲输出;fgets,linux,stream,popen,fgets,fcntl,Linux,Stream,Popen,Fgets,Fcntl,我正在使用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(

我正在使用
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)),但到目前为止没有成功

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


谢谢大家!

基于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
#包括
#包括
#包括
#包括
无效读取命令(常量字符*cmd)
{
文件*流;
int-fd;
国际旗帜;
字符缓冲区[1024];
fd_集fdset;
结构timeval超时;
int rc;
国际eof;
stream=popen(cmd,“r”);
fd=文件号(流);
eof=0;
而(!eof){
timeout.tv_sec=10;//10秒
timeout.tv_usec=0;
FD_零点(&fdset);
FD_集(FD和FD集);
rc=选择(fd+1和fdset、0、0和超时);
开关(rc){
案例1:{
//错误
如果(错误号!=EINTR){
fprintf(stderr,“select():错误“%m”(%d)\n”,错误号);
}
返回;
}
打破
案例0:{
//超时
printf(“超时\n”);
}
打破
案例1:{
//读物
rc=读取(fd,缓冲区,sizeof(缓冲区)-1);
如果(rc>0){
缓冲区[rc]='\0';
printf(“%s”,缓冲区);
fflush(stdout);
}
if(rc<0){
fprintf(stderr,“read():错误'%m'(%d)\n',错误号);
eof=1;
}
如果(0==rc){
//文件结束
eof=1;
}
}
打破
}//结束开关
}//结束时
pclose(流);
}
int main(int ac,char*av[])
{
read_cmd(av[1]);
返回0;
}//主要

本质上,fgets()仅在遇到EOF或行尾时返回。因此,如果您的程序只显示一行(即使是一个大行),即使基础文件描述符设置为非阻塞,fgets也不会在EOF或第一个换行符之前返回。感谢您的响应。我刚刚编辑了这个问题,以明确子进程输出的第一行包括一个换行符。尽管如此,fgets暂时不会读取它……这是一个竞争条件:程序在命令显示其第一行之前调用fgets()。因此,fgets()第一次返回NULL(如果在fgets()返回NULL时显示errno,则会得到EAGAIN)。因此,主程序在读取第一行之前进入sleep(),同时命令显示其所有输出。主程序在睡眠后会得到一切。所以,当我在上面说fgets()在遇到“\n”或EOF之前不会返回时,我错了。它和伊根一起回来了,只是睡了10毫秒。。。它一直调用fgets,直到EOF…是,但在睡觉前显示errno。您将获得EAGAIN,直到命令最终开始显示其输出。顺便说一下:睡眠(10)=10秒,而不是10毫秒