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毫秒