6

I want to get a Linux command's output string as well as command output status in a C++ program. I am executing Linux commands in my application.

for example: Command:

rmdir abcd

Command output string:

rmdir: failed to remove `abcd': No such file or directory

Command Status:

1 (Which means command has been failed)

I tried using Linux function system() which gives the output status, and function popen() which gives me output string of a command, but neither function gives me both the output string and output status of a Linux command.

1
  • 1
    Why is the question tagged C, if you are using C++? Commented Apr 2, 2013 at 12:48

5 Answers 5

7

The output string is in standard output or standard error descriptor (1 or 2, respectively).

You have to redirect these streams (take a look at dup and dup2 function) to a place, where you can read them (for example - a POSIX pipe).

In C I'd do something like this:

int pd[2];
int retValue;
char buffer[MAXBUF] = {0};
pipe(pd);
dup2(pd[1],1);
retValue = system("your command");
read(pd[0], buffer, MAXBUF);

Now, you have (a part of) your output in buffer and the return code in retValue.

Alternatively, you can use a function from exec (i.e. execve) and get the return value with wait or waitpid.

Update: this will redirect only standard output. To redirect standard error, use dup2(pd[1],1).

Sign up to request clarification or add additional context in comments.

7 Comments

Are you sure this works? I thought system() doesn't return until the shell process has exited. Also, if the child's output is large its writes to stdout may block, causing a deadlock. I guess I won't be surprised if it does work as the data is already in the pipe when the child exits, but still the deadlock may be an issue.
Well, this might block if the size of the output is larger than the size of a pipe (65536 by default on my machine). If this is expected, then fork + exec is the preferred way. But if not (I mean, if the case is known and the output size is bounded), than why bother? :)
Sure, fair enough. +1 for the interesting strategy
It's hard to imagine a case where the output size is really bound. The usual solution here is to redirect the output of the command to a temporary file, and then read that.
Ok, I've never used the word "guaranteed". By "bound" I've had in mind that you know the output to be reasonable in size, maybe because you're the one who generates the output. Of course, there is always a possibility of a disaster, bash failure, fork failure, !ANY! failure, but usually we just check for reasonable conditions. If the system is not critical/generic/widely used, than maybe using the first option that comes to your mind is good enough. But I vote for your answer anyway, because we can assume that you have enough disk storage space for your output :P
|
3

The simplest solution is to use system, and to redirect standard out and standard error to a temporarly file, which you can delete later.

Comments

2

Unfortunately there's no easy and simple way in C on Linux to do this. Here's an example how to read/write stdout/stderr/stdin of child process correctly.

And when you want to receive exit code you have to use waitpid (complete example is provided on the bottom of the provided page):

endID = waitpid(childID, &status, WNOHANG|WUNTRACED);

Now you just have to join those two together :)

There's also a great free book named Advanced Linux Programming (ALP) containing detailed information about these kinds of problem available here.

2 Comments

And there is an entire chapter in advancedlinuxprogramming.com on these issues
@BasileStarynkevitch ah, ALP, totally forgotten about that... I've add it to answer.
1

you can use popen system call, it will redirect output to a file and from file you can redirect output to a string. like :

    char buffer[MAXBUF] = {0};
    FILE *fd = popen("openssl version -v", "r");
    if (NULL == fd)
    {
        printf("Error in popen");
        return;
    }
    fread(buffer, MAXBUF, 1, fd);
    printf("%s",buffer);

    pclose(fd);

For more information read man page for popen.

Comments

1

Building on Piotr Zierhoffer answer above, here's a function that does just that, and also restores stdout and stderr their original state.

// Execute command <cmd>, put its output (stdout and stderr) in <output>,
// and return its status
int exec_command(string& cmd, string& output) {
    // Save original stdout and stderr to enable restoring
    int org_stdout = dup(1);
    int org_stderr = dup(2);

    int pd[2];
    pipe(pd);

    // Make the read-end of the pipe non blocking, so if the command being
    // executed has no output the read() call won't get stuck
    int flags = fcntl(pd[0], F_GETFL);
    flags |= O_NONBLOCK;

    if(fcntl(pd[0], F_SETFL, flags) == -1) {
        throw string("fcntl() failed");
    }

    // Redirect stdout and stderr to the write-end of the pipe
    dup2(pd[1], 1);
    dup2(pd[1], 2);
    int status = system(cmd.c_str());
    int buf_size = 1000;
    char buf[buf_size];

    // Read from read-end of the pipe
    long num_bytes = read(pd[0], buf, buf_size);

    if(num_bytes > 0) {
        output.clear();
        output.append(buf, num_bytes);
    }

    // Restore stdout and stderr and release the org* descriptors
    dup2(org_stdout, 1);
    dup2(org_stderr, 2);
    close(org_stdout);
    close(org_stderr);

    return status;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.