Given the following answer (first c++11 answer):
How do I execute a command and get the output of the command within C++ using 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;
}
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?
update
See my complete example here with the output:
Start
1 res: /home
2 res: stdout
stderr
3 res:
End
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).
External Libs
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 :)
Complete Solution Using Comments/Answer
Thanks all for your answers / comments, here is the modified solution (and runable):
working-solution
You can use the popen and pclose functions to pipe to and from processes. The popen() function opens a process by creating a pipe, forking, and invoking the shell. We can use a buffer to read the contents of stdout and keep appending it to a result string and return this string when the processes exit.
You can get the output after running a script using a pipe. We use pipes when we want the output of the child process. So here is the script, which you want to run. Put it in a command variable with the arguments your script takes (nothing if no arguments).
system() in C/C++ system() is used to invoke an operating system command from a C/C++ program. int system(const char *command);
POSIX system() function: Using the POSIX system() function, you can call z/OS UNIX services shell programs. You cannot use the POSIX system() function to call commands, EXECs, CLISTs, or executable modules under MVS and TSO/E. The POSIX system() function passes string to the sh shell command for execution.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With