How to Execute a Command and Get Return Code Stdout and Stderr of Command in C++

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

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(), buffer.size(), 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.

How do I execute a command and get the output of the command within C++ using POSIX?

#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::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}

Pre-C++11 version:

#include <iostream>
#include <stdexcept>
#include <stdio.h>
#include <string>

std::string exec(const char* cmd) {
char buffer[128];
std::string result = "";
FILE* pipe = popen(cmd, "r");
if (!pipe) throw std::runtime_error("popen() failed!");
try {
while (fgets(buffer, sizeof buffer, pipe) != NULL) {
result += buffer;
}
} catch (...) {
pclose(pipe);
throw;
}
pclose(pipe);
return result;
}

Replace popen and pclose with _popen and _pclose for Windows.

How To: Execute command line in C#, get STD OUT results

// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "YOURBATCHFILE.bat";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();

Code is from MSDN.

How to capture the exit_code and stderr of the command that is run in C++?

If you are interested in the error code, this is a more portable way of getting it rather than dividing by 256:

printf("Exit code: %i\n", WEXITSTATUS(pclose(fp)));

However, popen is one way, so you are either creating further workarounds by the usual redirection style in shell, or you follow this untested code to do it right:

#include <unistd.h>
#include <stdio.h>

/* since pipes are unidirectional, we need two pipes.
one for data to flow from parent's stdout to child's
stdin and the other for child's stdout to flow to
parent's stdin */

#define NUM_PIPES 2

#define PARENT_WRITE_PIPE 0
#define PARENT_READ_PIPE 1

int pipes[NUM_PIPES][2];

/* always in a pipe[], pipe[0] is for read and
pipe[1] is for write */
#define READ_FD 0
#define WRITE_FD 1

#define PARENT_READ_FD ( pipes[PARENT_READ_PIPE][READ_FD] )
#define PARENT_WRITE_FD ( pipes[PARENT_WRITE_PIPE][WRITE_FD] )

#define CHILD_READ_FD ( pipes[PARENT_WRITE_PIPE][READ_FD] )
#define CHILD_WRITE_FD ( pipes[PARENT_READ_PIPE][WRITE_FD] )

void
main()
{
int outfd[2];
int infd[2];

// pipes for parent to write and read
pipe(pipes[PARENT_READ_PIPE]);
pipe(pipes[PARENT_WRITE_PIPE]);

if(!fork()) {
char *argv[]={ "/usr/bin/bc", "-q", 0};

dup2(CHILD_READ_FD, STDIN_FILENO);
dup2(CHILD_WRITE_FD, STDOUT_FILENO);

/* Close fds not required by child. Also, we don't
want the exec'ed program to know these existed */
close(CHILD_READ_FD);
close(CHILD_WRITE_FD);
close(PARENT_READ_FD);
close(PARENT_WRITE_FD);

execv(argv[0], argv);
} else {
char buffer[100];
int count;

/* close fds not required by parent */
close(CHILD_READ_FD);
close(CHILD_WRITE_FD);

// Write to child’s stdin
write(PARENT_WRITE_FD, "2^32\n", 5);

// Read from child’s stdout
count = read(PARENT_READ_FD, buffer, sizeof(buffer)-1);
if (count >= 0) {
buffer[count] = 0;
printf("%s", buffer);
} else {
printf("IO Error\n");
}
}
}

The code is from here:

http://jineshkj.wordpress.com/2006/12/22/how-to-capture-stdin-stdout-and-stderr-of-child-program/

How to get the correct exit code of a shell command executed via pipe using popen() and pclose() in a daemonized process?

The issue with my program was, that the signal SIGCHLD was not handled by my program.

After evaluating errno and checking the error given by pclose() I was able to find a solution. errno returned ECHILD which means "No child processes" according to the errno man page. I caught this error by adding some more handling to my pclose_wrapper lambda function:

...
auto pclose_wrapper = [&rc](FILE *cmd) {
rc = pclose(cmd);
if (rc < 0) {
/* Log the error if pclose returns "-1" signaling an error occured */
syslog(LOG_ERR, "rc is negativ - %s", strerror(errno));
}
};
...

After some more research and looking at the man page for signal I found that my program was ignoring the SIGCHLD signal as mentioned before. This signal informs a process if a child process stopped or terminated.

The solution was to add sigaction(SIGCHLD, &newSigAction, NULL); and throwing out the line signal(SIGCHLD, SIG_IGN); which explicitly ignored the signal.

Here is the working code:

#include <array> // For std::array
#include <cstring>
#include <memory> // For std::unique_ptr
#include <string>
#include <sys/stat.h>
#include <sys/syslog.h> // For all syslog things
#include <sys/wait.h>
#include <unistd.h>

void signalHandler(int sig) {
switch (sig) {
case SIGINT:
case SIGTERM:
break;
case SIGCHLD:
/* Some child related action */
break;
}
}

int main(int argc, char *argv[]) {
/* Open log file to be able to use syslog */
setlogmask(LOG_UPTO(LOG_DEBUG));
openlog("MyDemoProg", LOG_PID, LOG_DAEMON);

#if 1 // Set to 0 to disable the daemonizing

pid_t pid = fork();

if (pid < 0)
exit(EXIT_FAILURE);

if (pid > 0)
exit(EXIT_SUCCESS);

if (setsid() < 0)
exit(EXIT_FAILURE);

struct sigaction newSigAction;
newSigAction.sa_handler = signalHandler;
sigemptyset(&newSigAction.sa_mask);
newSigAction.sa_flags = 0;

sigaction(SIGHUP, &newSigAction, NULL); /* catch hangup signal */
sigaction(SIGTERM, &newSigAction, NULL); /* catch term signal */
sigaction(SIGINT, &newSigAction, NULL); /* catch interrupt signal */
sigaction(SIGCHLD, &newSigAction,
NULL); /* catch child stopped or terminated signal */

pid = fork();
if (pid < 0)
exit(EXIT_FAILURE);

if (pid > 0)
exit(EXIT_SUCCESS);

umask(0);
chdir("/");
for (int x = sysconf(_SC_OPEN_MAX); x >= 0; x--) {
close(x);
}
syslog(LOG_DEBUG, "Daemonizing is enabled");
#else
syslog(LOG_DEBUG, "Daemonizing is disabled");
#endif

std::string command = "ls /var/bla/; sleep 2; echo test";
syslog(LOG_DEBUG, "Command is: %s", command.c_str());

int rc = -999;
std::array<char, 16> buffer;
std::string commandResult;

// A wrapper function to be able to get the return code while still using the
// automatic close function wizzardy of unique_ptr
auto pclose_wrapper = [&rc](FILE *cmd) {
rc = pclose(cmd);
if (rc < 0) {
/* Log the error if pclose returns "-1" signaling an error occured */
syslog(LOG_ERR, "rc is negativ - %s", strerror(errno));
}
};
{
const std::unique_ptr<FILE, decltype(pclose_wrapper)> pipe(
popen(command.c_str(), "r"), pclose_wrapper);

if (!pipe) {
syslog(LOG_ERR, "Could not open pipe! Exiting");
return EXIT_FAILURE;
}

/* Read in the pipe and save the content to a buffer */
while (::fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
commandResult += buffer.data();
}
}
syslog(LOG_DEBUG, "Command result is: %s", commandResult.c_str());
syslog(LOG_DEBUG, "Return code is: %d", rc);

return EXIT_SUCCESS;
}

And here is the output of both the non-daemonized and daemonized version:

(I added a syslog message to indicate if the daemonizing code was enabled or disabled.)

May 10 09:24:30 MY-EMBEDDED-DEVICE daemon.debug MyDemoProg[10872]: Daemonizing is disabled
May 10 09:24:30 MY-EMBEDDED-DEVICE daemon.debug MyDemoProg[10872]: Command is: ls /var/bla/; sleep 2; echo test
May 10 09:24:32 MY-EMBEDDED-DEVICE daemon.debug MyDemoProg[10872]: Command result is: test
May 10 09:24:32 MY-EMBEDDED-DEVICE daemon.debug MyDemoProg[10872]: Return code is: 0
---
May 10 09:24:49 MY-EMBEDDED-DEVICE daemon.debug MyDemoProg[10881]: Daemonizing is enabled
May 10 09:24:49 MY-EMBEDDED-DEVICE daemon.debug MyDemoProg[10881]: Command is: ls /var/bla/; sleep 2; echo test
May 10 09:24:51 MY-EMBEDDED-DEVICE daemon.debug MyDemoProg[10881]: Command result is: test
May 10 09:24:51 MY-EMBEDDED-DEVICE daemon.debug MyDemoProg[10881]: Return code is: 0

Now both versions give the expected return code of "0".

How to store command output and exitcode from terminal in a variable

Assuming the code provided in the article is correct and does what it should do, it is already doing all you need. The exit code and output are returned from the exec function.

You only have to use the returned CommandResult and access its members:

int main() {
auto result = Command::exec("echo blablub");
std::cout << result.output << "\n":
std::cout << result.exitstatus << "\n";
}

Getting STDOUT, STDERR, and response code from external *nix command in perl

Actually, the proper way to write this is:

#!/usr/bin/perl
$cmd = 'lsss';
my $out=qx($cmd 2>&1);
my $r_c=$?;
print "output was $out\n";
print "return code = ", $r_c, "\n";

You will get a '0' if no error and '-1' if error.

Capture both stdout and stderr in Bash

There is no way to capture both without temp file.

You can capture stderr to variable and pass stdout to user screen (sample from here):

exec 3>&1                    # Save the place that stdout (1) points to.
output=$(command 2>&1 1>&3) # Run command. stderr is captured.
exec 3>&- # Close FD #3.

# Or this alternative, which captures stderr, letting stdout through:
{ output=$(command 2>&1 1>&3-) ;} 3>&1

But there is no way to capture both stdout and stderr:

What you cannot do is capture stdout in one variable, and stderr in another, using only FD redirections. You must use a temporary file (or a named pipe) to achieve that one.



Related Topics



Leave a reply



Submit