Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to execute a command and get return code stdout and stderr of command in C++

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

like image 716
code_fodder Avatar asked Sep 04 '18 10:09

code_fodder


People also ask

How do you execute a command and get the output of command within C++?

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.

How do I find the output of a program?

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).

How do you run a system command in C++?

system() in C/C++ system() is used to invoke an operating system command from a C/C++ program. int system(const char *command);

Which function is used for running command string from within a program in Unix?

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.


1 Answers

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.

like image 50
Patrick B. Avatar answered Sep 27 '22 23:09

Patrick B.