How to execute a command and get return code stdout and stderr of command in C++
There is kind of workaround possible. You may redirect the stderr to stdout by appending "2>&1" to your cmd. Would this suit your needs?
From the man-page of popen
:
The pclose() function waits for the associated process to terminate and returns the exit status of the command as returned by wait4(2).
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;
}
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.
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.
You can get the return code from the pipe by using a custom deleter as such:
#include <cstdio>
#include <iostream>
#include <memory>
#include <string>
#include <array>
#include <utility>
using namespace std;
pair<string, int> exec(const char* cmd) {
array<char, 128> buffer;
string result;
int return_code = -1;
auto pclose_wrapper = [&return_code](FILE* cmd){ return_code = pclose(cmd); };
{ // scope is important, have to make sure the ptr goes out of scope first
const unique_ptr<FILE, decltype(pclose_wrapper)> pipe(popen(cmd, "r"), pclose_wrapper);
if (pipe) {
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
}
}
return make_pair(result, return_code);
}
int main(int argc, char* argv[]) {
if (argc <= 1) return 0;
cout << "calling with " << argv[1] << '\n';
const auto process_ret = exec(argv[1]);
cout << "captured stdout : " << '\n' << process_ret.first << endl;
cout << "program exited with status code " << process_ret.second << endl;
return 0;
}