文章

使用 popen 代替 system 函数执行系统命令

使用 popen 代替 system 函数执行系统命令

在 c/c++ 编程中常常需要执行一些系统命令,可以使用 system 函数,也可以采用 popen 的方式来实现,本文介绍 popen 的使用方法及优势。

system vs popen

特性systempopen
获取输出❌ 无法获取✅ 可实时读取
灵活性❌ 简单粗暴✅ 逐行处理
错误处理❌ 复杂✅ 精确状态码
效率❌ 低(启动shell)✅ 高(直接通信)

实现详解

command.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#ifndef COMMAND_H
#define COMMAND_H

#include <stdio.h>
#include <sys/wait.h>
#include <string>
#include <sstream>
#include <functional>

class Command {
public:
    using Callback = std::function<void(const std::string &output)>;

public:
    int execute(const std::string &command, const Callback &callback = nullptr) {
        FILE *fp = nullptr;
        std::stringstream output;
        /// 使用 popen 执行命令并获取输出
        fp = popen(command.c_str(), "r");
        if (!fp) {
            return -1;
        }
        
        char buffer[4096];
        /// 读取命令输出,直到结束
        while (nullptr != fgets(buffer, sizeof(buffer), fp)) {
            output << buffer;
        }

        /// 调用回调函数处理输出
        if (callback && !output.str().empty()) {
            callback(output.str());
        }
        
        /// 关闭管道并获取命令执行状态
        int status = pclose(fp);
        fp = nullptr;
        
        /// pclose 失败
        if (-1 == status) {
            return -2;
        }
        
        /// 命令未正常退出
        if (!WIFEXITED(status)) {
            return -3;
        }
        
        /// 返回命令退出状态码
        return WEXITSTATUS(status);
    }
};
#endif // COMMAND_H

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include "command.h"
#include <iostream>

int main() {
    Command cmd;
    int result = cmd.execute("ls -l", [](const std::string &output) {
        std::cout << output;
    });
    if (result < 0) {
        std::cerr << "Command execution failed with code: " << result << std::endl;
    }

    return 0;
}

代码说明

核心步骤

步骤说明
popen打开命令的标准输出管道
fgets逐行读取输出
Callback处理输出结果
pclose关闭管道获取状态码
WIFEXITED/WEXITSTATUS校验和提取退出状态

注意事项

  • 命令注入:避免直接拼接用户输入,使用白名单验证
本文由作者按照 CC BY 4.0 进行授权