popen其实就是获取一个外部程序的标准输出的的函数,整个调用过程如下所示:
popen实现过程
他是一种进程间通信方式,通过创建的管道完成外部程序信息的传递,优点是能够利用到强大的shell,缺点是额外启动shell成本较高,单向读写。
在Linux系统提供的函数中execl也可以用来启动一个新的进程,可以调用一个外部程序到当前的进程空间里,但是不是一个新的进程。int system(const char * string);也可启动一个shell。
一、什么是管道
管道是连接两个进程的介质,进程A数据通过管道流入进程B。对于Linux用户而言,对下面的语句都不会陌生:
cmd1|cmd2
管道与缓冲区非常相似,cmd1的输出结果将会存放在管道中,管道再转发这个输出结果,对于cmd2而言,转发的内容为输入。
二、最简单的popen
popen简单来说就是打开一个终端并运行指定程序,将其输出放入管道,返回一个指针方便取出管道内容。
#include <stdio.h>
FILE * popen(const char * command, const char * open_mode);
int pclose(FILE * stream_to_close);
输入参数
- const char * command 是一个shell命令或者程序;
- const char * open_mode 取值"r"或"w”
返回值
- FILE * fp 根据open_mode决定这个文件指针可以读还是写
三、例子
3.1 传递一小段数据
//test.cpp
#include <unistd.h>
#include <stdlib.h>
#inlcude <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int main()
{
FILE * read_fp=popen("./app","r");
if(!read_fp) cout<<"pipe open failed"<<endl;//BUFSIZ是默认缓冲区大小
char data[BUFSIZ+1];
memset(data,'\0',sizeof(data));
int chars_read=fread(data,sizeof(char),BUFSIZ,read_fp);//从标准输出打印,fread(名字,单位,长度,文件流)
if(chars_read>0)
{
printf("read data from app, message is :%s",data);
pclose(read_fp);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
//app.cpp
#include <stdlib.h>
#include <stdio.h>
int main()
{
double pi=3.141592653;
printf("%lf",pi);//该进程标准输出
}
进行编译产生两个应用程序test和app。
g++ test.cpp -o test
g++ app.cpp -o app
运行./test
,将会在shell启动app
并将在test
中使用app
的输出,可以看出两个进程如果需要双向通信需要用到两个管道。
3.2 传递更多数据
上面的例子都只是将所有数据通过一次fread和fwrite来调用或接收,有时候我们希望能以块形式发送数据,或者我们根本不知道输出数据的长度,为了避免定义一个非常大的缓冲区,我们可以用多个fread和fwrite将调用数据分为几部分处理。
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
FILE * read_fp;
char buffer[BUFSIZ+1];//留下一个作为\0终止符存储
int chars_read;
memset(buffer,'\0',sizeof(buffer));
read_fp=popen("ps ax","r");//打开另一个程序
if(read_fp!=NULL)//成功打开指定程序
{
chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);//fread返回实际读到的数量
while(chars_read>0)//只要有数据可读,一直进行下去
{
buffer[chars_read-1]='\0';//处理成C字符串
printf("Reading %d: -n\n %s\n",BUFSIZ,buffer);
chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);
}
pclose(read_fp);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
3.3 popen执行shell命令
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
FILE * read_fp;
char buffer[BUFSIZ+1];
int chars_read;
memset(buffer,'\0',sizeof(buffer));
read_fp=popen("cat *.h|wc -l","r");
if(read_fp!=NULL)
{
chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);
while(chars_read>0)
{
buffer[chars_read-1]='\0';
printf("Reading %d: -n\n %s\n",BUFSIZ,buffer);
chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);
}
pclose(read_fp);
exit(EXIT_SUCCESS);
}
exit(EXIT_FAILURE);
}
wc -l将会统计输出指定文件的行数。