C/C++执行命令并获取输出结果

在C/C++中执行指定命令是比较常见的功能,但是其输出对调用者来讲是非常重要的参考,亦或者通过获知调用程序运行成功与否做下一步处理。

Win32平台方法

Win32平台上的程序员貌似总不是对返回码那么感兴趣,不像Linux程序员随随便便在脚本都是 $?,但事实上确实是有专门的API提供了方法,而且总体上API功能很强大,也使用起来较复杂,此处只以最简单的功能作为介绍,读者可自行参考修改。

int RunCmd(const std::string& cmd, int& code, std::string& out) {
SECURITY_ATTRIBUTES sa;
HANDLE hRead, hWrite;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;

if (!CreatePipe(&hRead, &hWrite, &sa, 0)) {
DWORD ret = GetLastError();
return ret ? ret : -1;
}

STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));

si.cb = sizeof(STARTUPINFO);
GetStartupInfo(&si);
si.hStdError = hWrite;
si.hStdOutput = hWrite;
si.wShowWindow = SW_HIDE;
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;

char cmdline[200] = { 0 };
sprintf(cmdline, "%s", cmd.c_str());
if (!CreateProcess(NULL, cmdline, NULL, NULL, TRUE, NULL,
NULL, NULL, &si, &pi)) {
DWORD ret = GetLastError();
CloseHandle(hRead);
CloseHandle(hWrite);
return ret ? ret : -1;
}

CloseHandle(hWrite);
char buffer[4096] = { 0 };
DWORD bytesRead;
while (true) {
if (!ReadFile(hRead, buffer, 4095, &bytesRead, NULL)) break;
out.append(buffer, bytesRead);
Sleep(100);
}

DWORD exitCode = 0;
GetExitCodeProcess(pi.hProcess, &exitCode);
code = exitCode;
CloseHandle(hRead);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return 0;
}

其中接口的各个参数如下

  • 返回值,如果调用使用返回 -1 或者错误码,调用成功返回 0
  • 参数 cmd,是要执行的命令行命令
  • 参数 code,是执行命令的返回码,一般 0 表示成功(当然取决于程序自己的退出定义了)
  • 参数 out,执行命令的标准输出和错误输出,即该程序输出的信息内容

Linux平台方法

POSIX平台上实现这种功能其实就非常简单了,API也更容易使用,直接上代码

int RunCmd(const std::string& cmd, int& code, std::string& out) {
FILE* fp = popen(cmd.c_str(), "r");
if (!fp) {
return errno == 0 ? -1 : errno;
}

char buffer[4096] = { 0 };
while(!feof(fp)){
size_t len = fread(buffer, 1, 4096, fp);
if(len > 0) out.append(buffer, len);
}

return pclose(fp);
}

接口参数和意义说明见上面。

测试验证和其他问题

函数实现了,可以进行测试了,测试使用上面的代码,最简单的使用Shell脚本(Win下的批处理),比如

#!/bin/sh  #这是Linux上的,Win下不需要
exit 1

上面仅提供了退出码操作,正常执行的程序都有退出码的,跟上面其实类似,想想C语言的 main 函数,比如

int main(){
return 0; //这就是返回退出码
}

写个程序调用验证一下,代码简单,功能齐全。

上面程序执行命令并没有其他方面的设置,比如切换运行目录等等,如果确实需要这样的功能,

  • WinAPI直接有参数定义
  • Linux下可能需要自己 fork 和处理程序

另外一般的程序可以考虑设置一个超时参数,通过异步处理或者起线程读取,并通过定时关闭实现功能。

最近的文章

BAT脚本自动获取管理员权限等功能

偶尔可能会用到Windows下批处理脚本,虽然对BAT脚本使用的少,但是确实很有用,在此记录下一些常用的功能实现代码,长期更新。 自动获取管理员权限在Windows上很多操作需要管理员权限,比如创建服务等。但是在批处理在需要管理器权限的时候,一般要求操作人员切换到管理器 CMD 或 PS 下,进行执 …

技术 继续阅读
更早的文章

AS无法连接真机的解决方案

调试Android程序的时候,如果需要摄像头了,虚拟机就用不上了,需要连接真机。虽然连接真机很容易,但是后续因为Win10系统原因造成又无法连接成功,因此针对连接问题总结其解决方案。 基本方法一般来说,当我们将手机插入电脑后,如果之前没安装过驱动,则基本是会出现设备管理器有不可识别的设备。这种情况下 …

技术 继续阅读