前言
在程序开发中,经常需要通过执行命令行
操作来拿到一些系统信息,比如获取进程信息
,获取系统所有用户
等等,在这种情况下我们不但需要执行命令行,还需要拿到命令行的返回结果
。C++
提供了一个system
函数,可以执行指定的cmd
,但是只能返回一个执行结束的状态
,不能获得执行后的结果,很多场景下都是没办法满足我们的需求的,所以需要我们自己来造轮子。
1.执行操作
除了system函数以外,我们还可以通过popen
这个函数,这个函数的含义是
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen()函数的作用是:通过创建管道、fork、并调用shell。因为根据定义,管道是单向的,类型参数可以只指定读或写作,不是两者兼而有之;结果流相应地被读取只写或只写。
用法很简单,准备好cmd,对于type
,由于我们是读取command的输出
而不是向command输入参数,所以设置为"r"
,然后popen会返回一个文件结构体指针
,用于读取
执行完命令的输出
,如果type是"w"
的话,则这个文件结构体指针用于向cmd输入。
执行cmd的逻辑为
#include <stdio.h>
int execCmd(const char* cmd, std::string& result){
FILE* pipe = popen(cmd, "r");
if(!pipe){
printf("popen error\n");
return -1;
}
return 0;
}
接下来就是读取
cmd的输出
2.读取操作
读取pipe的内容有多种方式,可以通过getline
,fgets
以及fread
来读取
fgets读取
int execCmdUseFgets(const char *cmd, std::string &result)
{
FILE *pipe = popen(cmd, "r");
if (!pipe)
{
printf("popen error\n");
return -1;
}
size_t ret = 0;
char buf[bufferLen + 1] = {
0};
while (fgets(buf, bufferLen, pipe) != NULL)
{
result.append(buf);
}
printf("buf=%s\n",result.c_str());
return 0;
}
getline读取
int execCmdUseGetline(const char *cmd, std::string &result)
{
FILE *pipe = popen(cmd, "r");
if (!pipe)
{
printf("popen error\n");
return -1;
}
size_t ret = 0;
char* buf = nullptr;
size_t len = bufferLen;
ssize_t ret = 0;
while ((ret = getline(&buf, &len, pipe)) != -1)
{
result.append(buf);
}
printf("buf=%s\n", result.c_str());
return 0;
}
fread读取
int execCmd(const char* cmd, std::string& result){
FILE* pipe = popen(cmd, "r");
if(!pipe){
TdError("popen error");
return -1;
}
size_t ret = 0;
char buf[bufferLen+1] = {
0};
while ((ret = fread(buf, sizeof(char),bufferLen, pipe)) == bufferLen){
result.append(buf);
}
if(ferror(pipe) != 0) {
TdError("read pipe error");
return -1;
}
if(feof(pipe) != 0) {
TdError("exec cmd:%s success");
result.append(buf, ret);
}
return 0;
}
三种读取方式都可以最终拿到返回数据,不同的是,fgets
和getline
每次都只会读取一行
,即使buf很大,每次也只读一行,这意味着命令行有多少行的输出,while循环就要执行
多少次;而fread
不需要换行,buf长度为多少,每次最多
就能读多少字节的内容,具体用哪种方式需要结合具体的场景
。
如果每次执行cmd的输出数据的长度起伏不大,可以直接设置缓存区长度为一个cmd输出的长度,一次
即可读取完毕。如果输出的数据每次都起伏很大,可以设置一个合适的buf长度,来多次
读取。