# Linux C++运行shell命令增加超时机制
# 背景
在开发过程中,很多时候会用编程调用系统shell命令行运行一些第三方程序,然后获取输出。然而有些命令执行时间很长或者卡住,这样会导致程序无法继续下一步逻辑,所以需要一个超时机制去检查,如果超时了杀死命令进程。
这是俺使用的工具函数,创建指定命令行执行,然后获取输出,同时可以指定超时时间。
主要是通过select机制获取输出同时判断是否超时
#include <iostream>
#include <sys/wait.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
using namespace std;
/**
* @brief 执行shell命令
* @param cmd 命令
* @param argv 参数列表
* @param result 运行输出
* @param timeout 运行超时时间(秒)
* @return 命令的退出代码
*/
int RunProcess(const string& cmd, char* const argv[], string& result, int timeout)
{
int fd_out[2];
int fd_err[2];
int status = -1;
pipe(fd_out);
pipe(fd_err);
pid_t pid = fork();
if (pid == 0) {
close(fd_out[0]);
close(fd_err[0]);
dup2(fd_out[1], STDOUT_FILENO);
dup2(fd_err[1], STDERR_FILENO);
execvp(cmd.c_str(), argv);
// execvp调用失败,直接返回-1
perror("execvp error");
exit(-1);
}
else if (pid > 0) {
close(fd_out[1]);
close(fd_err[1]);
char buffer[1024] = { 0 };
fd_set readSet;
time_t startTime = time(NULL),curTime = 0;
while (1)
{
FD_ZERO(&readSet);
FD_SET(fd_out[0], &readSet);
FD_SET(fd_err[0], &readSet);
int fdMax = max(fd_out[0], fd_err[0]);
struct timeval tv = { 1, 0 }; // 1 seconds, 0 microseconds;
curTime = time(NULL);
if (timeout != -1 && (curTime - startTime > timeout))
{
printf("exec timeout!\n");
kill(pid, SIGTERM);
if (waitpid(pid, &status, 0) > 0)//阻塞等待进程退出了
{
printf("child exit %d\n", status);
}
break;
}
int res = select(fdMax + 1, &readSet, NULL, NULL, &tv);
if (res < 0) {
//printf("timeout\n");
if (errno != EINTR)
{
printf("select() error %d %s\n", errno, strerror(errno));
}
break;//错误
}
else if (res == 0)
{
//超时,继续等待
if (waitpid(pid, &status, WNOHANG) > 0)//进程退出了
{
printf("child exit %d\n", status);
break;
}
continue;
}
if (FD_ISSET(fd_out[0], &readSet))//输出有数据了
{
int len = 0;
readmore:
len = read(fd_out[0], buffer, sizeof(buffer));
if (len > 0)
{
result.append(buffer, len);
if (len == sizeof(buffer))
{
goto readmore;
}
}
else {
printf("read error %d %s\n", len, strerror(errno));
if (waitpid(pid, &status, WNOHANG) > 0)//进程退出了
{
printf("child exit %d\n", status);
break;
}
break;
}
}
if (FD_ISSET(fd_err[0], &readSet))//输出有数据了
{
int len = 0;
readmore1:
len = read(fd_err[0], buffer, sizeof(buffer));
if (len > 0)
{
result.append(buffer, len);
if (len == sizeof(buffer))
{
goto readmore1;
}
}
else {
printf("read error %d %s\n", len, strerror(errno));
if (waitpid(pid, &status, WNOHANG) > 0)//进程退出了
{
printf("child exit %d\n", status);
break;
}
break;
}
}
}
close(fd_err[0]);
close(fd_out[0]);
}
//linux有些命令执行成功,状态码不一定是0,需要根据输出来判断
return status;
}
int main(int argc, char* argv[])
{
//参数格式,{程序名称,参数1,...,参数n,NULL},相当于main函数的argv参数
char* const param[] = { "ping","192.168.3.1",NULL };//必须以NULL结束
string resStr;
int status = RunProcess("ping", param, resStr,10);//超时10秒
printf("status:%d restult:%s\n",status,resStr.c_str());
}
编译运行
gcc -o runcmd main.cpp -lstdc++
./runcmd
程序运行ping 192.168.3.1命令,由于在linux ping命令默认是一直执行,这样可以模拟命令执行过久的场景,10秒后超时返回