C++调用system()函数的注意事项
1. 你以为的system()调用
在Linux下进行C++开发,经常需要调用一些系统的命令来执行特殊的功能,如创建目录、删除文件等。而C++标准库提供了system()函数来方便地调用系统命令。
你可能会写出类似下面的代码:
#include <iostream>
#include <cstdlib> // 包含 system() 函数声明
using namespace std;
int main() {
// 1. 执行 ls 命令(列出当前目录文件)
cout << "=== 执行 ls -l 命令 ===" << endl;
int ret = system("ls -l");
cout << "命令退出状态值:" << ret << endl;
// 2. 执行 mkdir 命令(创建目录)
cout << "\n=== 执行 mkdir test_dir 命令 ===" << endl;
ret = system("mkdir test_dir");
if (ret == 0) {
cout << "目录创建成功" << endl;
} else {
cout << "目录创建失败/已存在" << endl;
}
return 0;
}
你觉得上面的代码是正确的吗?
对于简单明确的指令,他是可用的。但是这个用法是不严谨,也不推荐的。
2. system()函数用法
2.1. 函数声明
// 头文件:<stdlib.h>
int system(const char *command);
2.2. 返回值详细说明
system() 函数的核心逻辑是:fork 子进程 → 子进程中 exec 执行命令 → 父进程 waitpid 等待子进程退出。其返回值不是命令的执行结果,而是整个调用流程的状态,需通过宏解析,具体规则如下:
| 返回值场景 | 含义 | 解析方式 |
|---|---|---|
返回 -1 | fork 失败 / waitpid 出错(除 EINTR),子进程未创建 | 直接判断,可通过 errno 获取具体错误(如 EAGAIN、ENOMEM) |
返回值 status != -1 | 子进程已创建,但需进一步解析子进程状态 | 用 <sys/wait.h> 中的宏解析 |
| WIFEXITED(status) = true | 子进程正常退出(exec 执行完成) | 用 WEXITSTATUS(status) 获取命令的退出码(0=成功,非0=命令执行失败) |
| WEXITSTATUS(status) = 127 | exec 执行失败(如命令不存在、权限不足),子进程正常退出但命令未执行 | 固定退出码 127,代表 exec 失败 |
| WIFSIGNALED(status) = true | 子进程被信号终止(如 kill -9 杀死) | 用 WTERMSIG(status) 获取终止信号编号,WCOREDUMP(status) 判断是否产生core |
2.3. 关键宏说明
WIFEXITED(status):判断子进程是否正常退出(非信号终止)。WEXITSTATUS(status):提取正常退出时的退出码(仅 WIFEXITED 为 true 时有效)。WIFSIGNALED(status):判断子进程是否被信号终止。WTERMSIG(status):提取终止子进程的信号编号(仅 WIFSIGNALED 为 true 时有效)。WCOREDUMP(status):判断子进程终止时是否产生 core dump 文件。
使用上面这些宏需要包含<sys/wait.h>头文件。
3. 函数封装
我们可以对system()函数做一个封装,用于对返回值的各种异常进行处理,提高函数的健壮性和易用性。
3.1. 函数声明
int SystemCall(const std::string& cmd)
3.2. 函数实现
#include <cstdint>
#include <cstring>
#include <iostream>
#include <string>
#include <sys/wait.h>
#include <unordered_map>
#include <vector>
std::string ParseExitCode(int exit_status)
{
// Common exit status code explanations
static const std::unordered_map<int, std::string> exitCodeMap = {
{ 0, "Command executed successfully" },
{ 1, "General error or command usage error" },
{ 2, "Command parameter error" },
{ 126, "Command not executable (insufficient permissions)" },
{ 127, "Command not found" },
};
auto it = exitCodeMap.find(exit_status);
if (it != exitCodeMap.end())
{
return it->second;
}
else
{
return "Unknown exit status code";
}
}
std::string ParseSignal(int signal)
{
// Common signal explanations
static const std::unordered_map<int, std::string> signalMap = {
{ SIGTERM, "SIGTERM (termination request)" },
{ SIGKILL, "SIGKILL (force termination)" },
{ SIGSEGV, "SIGSEGV (segmentation fault)" },
{ SIGINT, "SIGINT (interrupt, usually Ctrl+C)" },
{ SIGABRT, "SIGABRT (program abnormal termination)" },
};
auto it = signalMap.find(signal);
if (it != signalMap.end())
{
return it->second;
}
else
{
return "Unknown signal";
}
}
// Reference Doc: https://www.man7.org/linux/man-pages/man3/system.3.html
int SystemCall(const std::string& cmd)
{
pid_t status;
status = std::system(cmd.c_str());
std::cout << "status: " << status << std::endl;
if (-1 == status)
{
std::cerr
<< "Child process could not be created, or its status could not be retrieved. err msg:"
<< std::strerror(errno) << std::endl;
return status;
}
// (status & 0x7f ) == 0
// Child process exited normally (exec completed)
if (WIFEXITED(status))
{
// ((status) & 0xff00) >> 8
int exit_code = WEXITSTATUS(status);
std::cerr << "The child process exited normally, exit status:" << exit_code
<< ", exit msg: " << ParseExitCode(exit_code) << std::endl;
return exit_code;
}
// Child process terminated by signal
// ((signed char) (((status) & 0x7f) + 1) >> 1) > 0
if (WIFSIGNALED(status))
{
// (status) & 0x7f
int signal_code = WTERMSIG(status);
std::cerr << "Child process terminated by signal:" << signal_code
<< ", signal msg: " << ParseSignal(signal_code) << std::endl;
return signal_code;
}
// Other unexpected exit status (rare)
std::cout << "other error, status: " << status << std::endl;
return status;
}
测试代码
int main(int argc, const char** argv)
{
// Test cases for SystemCall function
std::vector<std::string> testCommands = {
"echo 'Hello, World!'", // Successful command
"false", // Command that exits with code 1
"nonexistentcommand", // Command not found
"sleep 1", // Another successful command
};
for (const auto& cmd : testCommands)
{
std::cout << "\n--- Testing command: " << cmd << " ---" << std::endl;
int ret = SystemCall(cmd);
std::cout << "Return value: " << ret << std::endl;
}
return 0;
}