linux - 如何从 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)
),但到目前为止还没有运气。
如何关闭缓冲以便实时读取输出?
谢谢!
解决方案
基于类似 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
推荐阅读
- angular - 如何模拟 History.state 以编写角度单元测试
- python-3.x - 读取数据时将字符串列转换为整数(熊猫)
- vb.net - 读取行的特定部分
- grafana - 缩小 x 轴以处理 Grafana 中的空数据
- javascript - 如何自动检查页面并使标签在 Javascript 中不可见
- python - Selenium:“列表”对象没有属性“文本”
- linux - 为什么 "A=3; A=4 echo $A" 在 Bash 中产生 3(而不是 4)?
- typescript - 打字稿强制对象文字没有按预期工作
- java - 我如何修改它以创建一个我可以输入的大小的随机数组?
- c# - PDFsharp - 绘制文本,围绕自定义枢轴点旋转并移动它