问题描述
我正在使用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
的文档中,我看到:
我已经尝试了一些方法来关闭缓冲(例如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,&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