微信公众号:二进制人生
专注于嵌入式linux开发。问题或建议,请发邮件至hjhvictory@163.com。
更新日期:2019/12/12
内容目录
函数原型基本原理简单实例项目级封装
函数原型
#include <stdio.h>
FILE *popen(const char *cmdstring, const char *type);
ret = 成功返回文件指针,失败返回NULL
int pclose(FILE *fp);
ret = cmdstring的终止状态,失败返回-1
基本原理
(1)popen()会调用fork()产生子进程,然后子进程里会调用execl函数来执行/bin/sh -c cmdstring。
父进程和子进程间通过管道pipe通信,如果type是“r”,则子进程会将管道的写端连接到标准输出,而父进程返回管道的读端fp,如下图:

我们对fp进行读,也就是在读取子进程执行命令的输出结果。
如果type是“w”,则子进程会将管道的读端连接到标准输入,而父进程返回管道的写端fp,如下图:

我们对fp进行写,也就是在向子进程的标准输入写数据。
(2)可以使用所有操作文件指针FILE*的函数,但fclose()除外。在调用完popen之后,务必调用pclose函数关闭返回的函数指针,实际上调用fclose也不会发生什么重大错误,pclose最终还是调用了fclose,只不过它会调用waitpid或者wait4函数等待子进程执行完毕再close掉fp。
(3)返回值:若成功则返回文件指针,否则返回NULL,错误原因存于errno中。
返回NULL的原因有很多:
参数非法,错误值errno = EINVAL;
分配内存失败;
调用pipe失败;
调用fork失败;
(4)popen函数不会阻塞,但是pclose函数会调用waitpid或者wait4函数等待子进程的执行结束,所以在调用pclose之后会阻塞,这一点是需要切记的。pclose函数的返回值是调用waitpid函数获取到的子进程的退出状态status。
注意:popen()会继承环境变量,通过环境变量可能会造成系统安全的问题。
简单实例
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE *fp;
char buffer[1024];
fp = popen("cat /etc/passwd", "r");
fgets(buffer, sizeof(buffer), fp);
printf("%s", buffer);
pclose(fp);
return 0;
}
项目级封装
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int POPEN_READ(char *cmd,char *res,int len)
{
len--;//预留一个位来保存'\0'
if(cmd == NULL || res == NULL)
return -1;
FILE *fp;
char buf[1024];
int l,nread = 0;
fp = popen(cmd,"r");
if(fp != NULL){
//之所以这么复杂,是因为我要处理传入的buf的len<输出的结果的情况。
while(fgets(buf,1024,fp) != NULL)
{
l = strlen(buf);
if(l >= len - nread)
{
memcpy(res + nread,buf,len-nread);
res[len+1] = '\0';
return len;
}
else
{
memcpy(res + nread,buf,l+1);
}
nread += l;
}
pclose(fp);
return nread;
}
else{
printf("popen % failed!",cmd);
return -1;
}
}
int main()
{
char buf[1024];
memset(buf,0,sizeof(buf));
POPEN_READ("ls /",buf,sizeof(buf));
printf("buf=%s\n",buf);
return 0;
}

文章转载自二进制人生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




