如何在C++中执行命令并获取命令的返回码stdout和stderr

2022-01-02 00:00:00 process stdout stderr c++ c++11

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

Given the following answer (first c++11 answer):

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

为了您的方便,以下是实现:

Here is the implementation for your convenience:

#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");)并将标准输出转换为字符串非常有效.

This works really nicely to execute a command (e.g. std::string res = exec("ls");) and get the stdout into a string.

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

But what it does not do is get the command return code (pass/fail integer) or the stderr. Ideally I would like a way to get all three (return code, stdout, stderr).

我会接受标准输出和标准错误.我想我需要添加另一个管道,但我真的看不到第一个管道是如何设置以获得标准输出的,所以我无法考虑如何更改它以获得两个.

I would settle for stdout and stderr. I am thinking that I need to add another pipe, but I can't really see how the first pipe is setup to get stdout so I can't think how I would change it to get both.

有人知道如何做到这一点,或者有其他可行的方法吗?

Any one got any ideas how to do that, or alternative approaches that may work?

更新

查看我的完整示例 此处 输出:

See my complete example here with the output:

Start
1 res: /home

2 res: stdout

stderr
3 res: 
End

您可以看到 3 res: 不像 2 res: stdout 那样打印 stderr,但 stderr 只是在单独的屏幕上转储到屏幕上逐个流程(而不是我的程序).

You can see that 3 res: does not print stderr in the same way that 2 res: stdout does, but stderr is just dumped onto the screen on a separate line by the process (and not my program).

外部库

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

I really don't want to use external libraries like Qt and boost - mostly because I want the portability of it and also many projects that I work on don't use boost. However I will mark up solutions that contain these options as they are valid for other users :)

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

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

Thanks all for your answers / comments, here is the modified solution (and runable):

工作解决方案

推荐答案

来自 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<> 的析构函数魔术)将为您提供进程的返回码(如果进程未终止,则阻塞).

So, calling pclose() yourself (instead of using std::shared_ptr<>'s destructor-magic) will give you the return code of your process (or block if the process has not terminated).

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;
}

使用 popen() 获取 stderr 和 stdout,恐怕您需要通过添加将 stderr 的输出从传递给 popen() 的命令行重定向到 stdout2>&1.这带来了两个流不可预测地混合的不便.

Getting stderr and stdout with popen(), I'm afraid you'd need to redirect the output of stderr to stdout from the command-line you're passing to popen() by adding 2>&1. This has the inconvinience that both streams are unpredictably mixed.

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

If you really want to have two distinguished file-descriptors for stderr and stdout, one way to do it is to do the forking yourself and to duplicate the new processes stdout/stderr to two pipes which are accessible from the parent process. (see dup2() and pipe()). I could go into more detail here, but this is quite a tedious way of doing things and much care must be taken. And the internet is full of examples.

相关文章