首页 > 解决方案 > 如何在 C++ 中执行命令并获取命令的返回码 stdout 和 stderr

问题描述

给出以下答案(第一个 c++11 答案):

如何使用 POSIX 在 C++ 中执行命令并获取命令的输出?

为了您的方便,这里是实现:

#include <cstdio>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <array>

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;
    std::shared_ptr<FILE> pipe(popen(cmd, "r"), pclose);
    if (!pipe) throw std::runtime_error("popen() failed!");
    while (!feof(pipe.get())) {
        if (fgets(buffer.data(), 128, pipe.get()) != nullptr)
            result += buffer.data();
    }
    return result;
}

这非常适合执行命令(例如std::string res = exec("ls");)并将标准输出转换为字符串。

但它不做的是获取命令返回码(通过/失败整数)或标准错误。理想情况下,我想要一种方法来获取所有三个(返回代码、标准输出、标准错误)。

我会满足于标准输出和标准错误。我在想我需要添加另一个管道,但是我真的看不到第一个管道是如何设置来获取标准输出的,所以我想不出如何改变它来获取两者。

任何人有任何想法如何做到这一点,或者可能有效的替代方法?

更新

在这里查看我的完整示例和输出:

Start
1 res: /home

2 res: stdout

stderr
3 res: 
End

您可以看到它3 res:不会以与打印相同的方式打印 stderr 2 res: stdout,但 stderr 只是由进程(而不是我的程序)在单独的行上转储到屏幕上。

外部库

我真的不想使用像 Qt 和 boost 这样的外部库——主要是因为我想要它的可移植性,而且我从事的许多项目也不使用 boost。但是,我将标记包含这些选项的解决方案,因为它们对其他用户有效:)

使用评论/答案的完整解决方案

感谢大家的回答/评论,这是修改后的解决方案(并且可运行):

工作解决方案

标签: c++c++11processstdoutstderr

解决方案


从手册页popen

The pclose() function waits for the associated process to terminate  and returns the exit status of the command as returned by wait4(2).

所以,调用pclose()你自己(而不是使用std::shared_ptr<>' 的析构函数)会给你你的进程的返回码(或者如果进程没有终止,则阻塞)。

std::string exec(const char* cmd) {
    std::array<char, 128> buffer;
    std::string result;

    auto pipe = popen(cmd, "r"); // get rid of shared_ptr

    if (!pipe) throw std::runtime_error("popen() failed!");

    while (!feof(pipe)) {
        if (fgets(buffer.data(), 128, pipe) != nullptr)
            result += buffer.data();
    }

    auto rc = pclose(pipe);

    if (rc == EXIT_SUCCESS) { // == 0

    } else if (rc == EXIT_FAILURE) {  // EXIT_FAILURE is not used by all programs, maybe needs some adaptation.

    }
    return result;
}

使用 获取 stderr 和 stdout popen(),恐怕您需要通过添加将 stderr 的输出从传递给 popen() 的命令行重定向到2>&1stdout 这带来的不便之处在于两种流都不可预测地混合在一起。

如果您真的想为 stderr 和 stdout 提供两个不同的文件描述符,一种方法是自己进行分叉并将新进程 stdout/stderr 复制到两个可从父进程访问的管道。(见dup2()pipe())。我可以在这里更详细地介绍,但这是一种相当乏味的做事方式,必须非常小心。互联网上到处都是例子。


推荐阅读