C/C++运行shell命令通常有两种办法,调用 system()
或者 popen()
函数
1. system函数
system()
函数原型:
/* Execute the given line as a shell command.
This function is a cancellation point and therefore not marked with
__THROW. */
extern int system (const char *__command) __wur;
可以发现, system()
函数会运行shell命令,并返回标志位,告知用户命令是否运行成功,但是不会返回shell指令的运行结果。
有时,我们需要shell命令的运行结果,比如我们写系统监控日志,我们需要把结果写到文件里
popen
函数可以帮助我们。
2. popen函数
popen()
函数原型:
/* Create a new stream connected to a pipe running the given command.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern FILE *popen (const char *__command, const char *__modes)
popen
函数的功能是这样的:创建一个子shell进程,运行__command指令;同时,会建立一个匿名管道用户当前进程和子进程之间的通信,用户可以通过这个管道,获取shell命令的运行结果,或是向shell进行写输入参数,popen
函数的返回值FILE*
就是指向这个管道。这个管道是一个单向管道,只能写或者读,不能同时读写,因此__modes只能是“r”或者“w”。
参考这篇文章(修改了几个小bug),封装了一个运行shell命令的函数;
纯C版:
#include <string.h>
#include <stdio.h>
#define CMD_RESULT_BUF_SIZE 1024
int ExecuteCMD_C(const char* cmd, char* result_)
{
char result[CMD_RESULT_BUF_SIZE] = {0};
char buf_temp[CMD_RESULT_BUF_SIZE] = {0};
FILE *ptr = NULL;
int iRet = -1;
//popen: 开启子进程,建立管道,并运行指令,'r':从子进程获取结果,'w':向子进程写数据
if((ptr = popen(cmd, "r")) != NULL) //popen
{
while(fgets(buf_temp, sizeof(buf_temp), ptr) != NULL)
{
if(strlen(result) + strlen(buf_temp) > CMD_RESULT_BUF_SIZE){
printf("shell返回结果超出buffer size,进行截断\n");
break;
}
strcat(result, buf_temp); //字符串拼接
}
strcpy(result_, result);
pclose(ptr);
ptr = NULL;
iRet = 0; // 处理成功
}
else
{
printf("popen %s error\n", cmd);
iRet = -1; // 处理失败
}
return iRet;
}
int main() {
char result_[CMD_RESULT_BUF_SIZE] ;
ExecuteCMD_C("ps -e|grep test2| grep -v 'grep' | awk '{print $1}'", result_);
printf("%s",result_);
ExecuteCMD_C("ls", result_);
printf("%s",result_);
}
.
.
C++ std::string
版:
#include <string.h>
#include <stdio.h>
#include <string>
#include <iostream>
#define CMD_RESULT_BUF_SIZE 1024
int ExecuteCMD_Cpp(const std::string cmd, std::string &result_)
{
result_ = {};
char buf_temp[CMD_RESULT_BUF_SIZE] = {0};
FILE *ptr = NULL;
int iRet = -1;
//popen: 开启子进程,建立管道,并运行指令,'r':从子进程获取结果,'w':向子进程写数据
if((ptr = popen(cmd.c_str(), "r")) != NULL) //popen
{
while(fgets(buf_temp, sizeof(buf_temp), ptr) != NULL)
{
result_ += buf_temp;
}
pclose(ptr);
ptr = NULL;
iRet = 0; // 处理成功
}
else
{
printf("popen %s error\n", cmd.c_str());
iRet = -1; // 处理失败
}
return iRet;
}
int main() {
std::string str;
ExecuteCMD_Cpp("ps -e|grep test2| grep -v 'grep' | awk '{print $1}'", str);
std::cout << str;
ExecuteCMD_Cpp("ls", str);
std::cout << str;
}
两个代码的运行结果是一致的。
C++的更好一些,不必担心字符串长度不够的问题。
参考:
https://zhuanlan.zhihu.com/p/351712885