IE盒子

搜索
查看: 75|回复: 0

C++实用轮子之执行命令行并获取输出

[复制链接]

1

主题

7

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2023-4-10 16:35:16 | 显示全部楼层 |阅读模式
前言

在程序开发中,经常需要通过执行命令行操作来拿到一些系统信息,比如获取进程信息,获取系统所有用户等等,在这种情况下我们不但需要执行命令行,还需要拿到命令行的返回结果。C++提供了一个system函数,可以执行指定的cmd,但是只能返回一个执行结束的状态,不能获得执行后的结果,所以需要我们自己来造轮子。
执行操作

除了system函数以外,我们还可以通过popen这个函数,这个函数的含义是
#include <stdio.h>

FILE *popen(const char *command, const char *type);

int pclose(FILE *stream);
popen()函数的作用是:通过创建管道、fork、并调用shell。因为根据定义,管道是单向的,类型参数可以只指定读或写作,不是两者兼而有之;结果流相应地被读取只写或只写。

用法很简单,准备好cmd,对于type,由于我们是读取command的输出而不是向command输入参数,所以设置为"r",然后popen会返回一个文件结构体指针,用于读取执行完命令的输出,如果type是"w"的话,则这个文件结构体指针用于向cmd输入。
执行cmd的逻辑为
#include <stdio.h>

int execCmd(const char* cmd, std::string& result){
    FILE* pipe = popen(cmd, "r");
    if(!pipe){
        printf("popen error\n");
        return -1;
    }
    return 0;
}
接下来就是读取cmd的输出
读取操作

读取pipe的内容有多种方式,可以通过getline,fgets以及fread来读取
fgets读取

int execCmdUseFgets(const char *cmd, std::string &result)
{
    FILE *pipe = popen(cmd, "r");
    if (!pipe)
    {
        printf("popen error\n");
        return -1;
    }
    size_t ret = 0;
    char buf[bufferLen + 1] = {0};
    while (fgets(buf, bufferLen, pipe) != NULL)
    {
        result.append(buf);
    }
    printf("buf=%s\n",result.c_str());
    return 0;
}

getline读取

int execCmdUseGetline(const char *cmd, std::string &result)
{
    FILE *pipe = popen(cmd, "r");
    if (!pipe)
    {
        printf("popen error\n");
        return -1;
    }
    size_t ret = 0;
    char* buf = nullptr;
    size_t len  = bufferLen;
    ssize_t ret = 0;
    while ((ret = getline(&buf, &len, pipe)) != -1)
    {
        result.append(buf);
    }
   
    printf("buf=%s\n", result.c_str());
    return 0;
}
fread读取

int execCmd(const char* cmd, std::string& result){
    FILE* pipe = popen(cmd, "r");
    if(!pipe){
        TdError("popen error");
        return -1;
    }
    size_t ret = 0;
    char buf[bufferLen+1] = {0};
    while ((ret = fread(buf, sizeof(char),bufferLen, pipe)) == bufferLen){
        result.append(buf);
    }
    if(ferror(pipe) != 0) {
        TdError("read pipe error");
        return -1;
    }
    if(feof(pipe) != 0) {
        TdError("exec cmd:%s success");
        result.append(buf, ret);
    }
    return 0;
}
三种读取方式都可以最终拿到返回数据,不同的是,fgets和getline每次都只会读取一行,即使buf很大,每次也只读一行,这意味着命令行有多少行的输出,while循环就要执行多少次;而fread不需要换行,buf长度为多少,每次最多就能读多少字节的内容,具体用哪种方式需要结合具体的场景。
如果每次执行cmd的输出数据的长度起伏不大,可以直接设置缓存区长度为一个cmd输出的长度,一次即可读取完毕。如果输出的数据每次都起伏很大,可以设置一个合适的buf长度,来多次读取。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表