21

I just want to know which is the best way to execute an external command in C++ and how can I grab the output if there is any?

Edit: I Guess I had to tell that I'm a newbie here in this world, so I think I'm gonna need a working example. For example I want to execute a command like:

ls -la

how do I do that?

Lipis
  • 21,388
  • 20
  • 94
  • 121

3 Answers3

24

Use the popen function.

Example (not complete, production quality code, no error handling):

FILE* file = popen("ls", "r");
// use fscanf to read:
char buffer[100];
fscanf(file, "%100s", buffer);
pclose(file);
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 10
    @Daniel This is not production code. This is an answer on an online forum that is supposed to give one the high-level idea of the popen function and its arguments. Anyone in the habit of treating all Stack Overflow answers as production code has more serious problems than this. Also, any buffer that has the possibility overflow is not a DEAD SERIOUS SECURITY THREAT WORTHY OF SHOUTING AT. Without a specific threat model and knowing what is trusted and what is not in the particular environment something is applied to, discussion about security is moot. – Mehrdad Afshari Apr 04 '13 at 00:34
  • @Daniel I think your first comment is going to dissuade most people. Why don't you provide a link to a CERT page. I am sure this is somewhere in the [secure programming notes](https://www.securecoding.cert.org/confluence/pages/viewpage.action?pageId=4063380). Mehrdad may even update his answer. – artless noise Apr 05 '13 at 00:09
  • 1
    @Daniel I hear you. Sure, a more complete answer warning about all possible gotchas could be a better answer. You might be able to write a book about gotchas in three lines of code. It is, however, clear that what's germane to this question is "popen" and not how you'd further process the output of the external process... – Mehrdad Afshari Apr 05 '13 at 00:40
  • 6
    ...I felt this answer, as is, is helpful enough for people who can figure the rest out. That's why I wrote it. Yes, it is not perfect. That's why Stack Overflow has all these nice features so that you can comment on things to be careful about, or, better yet, edit or contribute your own, better answer, instead of launching ad-hominem attacks against other volunteer contributors. This is especially disappointing considering I did post a warning that clearly implies recommending against direct use in production code. – Mehrdad Afshari Apr 05 '13 at 00:41
  • NO: You do not post code this dangerous anywhere. There is a big difference between "this code will halt with a segfault" and "this code will seem to work but allow an attacker in". The other answer below gives the correct usage; there is no excuse for your answer and it should be deleted. As an example of how dangerous the situation is, hospitals now have to not use life-saving equipment because attackers get in because of bugs like this. Bugs in software now kill people and allow organized crime to do major damage. You have no idea what you are talking about. – Daniel Apr 05 '13 at 04:24
  • 3
    @MehrdadAfshari You're too kind to have updated your answer; I hope people noticed. – artless noise Apr 05 '13 at 18:17
  • 1
    geez what happened here with the comments? – Katie Apr 11 '13 at 22:00
  • 2
    @Daniel I think it was pretty clear back then (and today as well) that in general executing external command as they are is not really a good practice in production for many reasons and among others is the fact that you can simply change that command to something else.. so it's pretty much common sense.. and no need to even mentioning anything extra (the list could be huge) since it answers exactly what is being asked! I would even revert the answer to the original :) – Lipis Jun 04 '13 at 09:23
  • `%100s` should be `%99s` to account for the NUL-terminator – Spikatrix Oct 27 '19 at 11:31
20

An example:

#include <stdio.h>

int main() {
    FILE * f = popen( "ls -al", "r" );
    if ( f == 0 ) {
        fprintf( stderr, "Could not execute\n" );
        return 1;
    }
    const int BUFSIZE = 1000;
    char buf[ BUFSIZE ];
    while( fgets( buf, BUFSIZE,  f ) ) {
        fprintf( stdout, "%s", buf  );
    }
    pclose( f );
}
  • forgot return 0 at the end of main. and use sizeof( buff ) instead 1000 in fgets :) – bayda Mar 22 '09 at 19:54
  • 3
    C++ does not require a return at end of main. –  Mar 22 '09 at 20:06
  • True, but it's nice to have the return, so that it's obvious what's going to come out of the program. – Michael Kohne Mar 22 '09 at 21:27
  • it worked perfectly with the ls.. but when I'm trying the command "wget -O image.jpg http://admin:admin@192.168.1.66/image.jpg" it works ok too.. but even if i remove the fprintf command, I'm still getting the output on my screen..! Any help on that? (No output if I'm doing that with ls) – Lipis Mar 22 '09 at 21:38
  • 3
    wget writes its status messages to stderr - run it with the --quiet option –  Mar 22 '09 at 21:46
  • hmm... But what if I want to catch that as well...? Without the --quite option, but letting be decide whenever I want to print it out or not.. – Lipis Mar 22 '09 at 22:15
  • FILE * f = popen( "wget www.google.com 2>&1", "r" ); // this assumes a bash shell –  Mar 22 '09 at 22:34
  • 2>&1 is valid Bourne/Posix shell syntax – camh Mar 23 '09 at 02:41
  • 4
    @Dave Jarvis Please do not feel free to "improve" my answers by adding unnecessary code. –  Feb 24 '10 at 10:42
17

popen definitely does the job that you're looking for, but it has a few drawbacks:

  • It invokes a shell on the command you're executing (which means that you need to untaint any user provided command strings)
  • It only works in one direction, either you can provide input to the subprocess or you can read its output.

If you want invoke a subprocess and provide input and capture output then you'll have to do something like this:

int Input[2], Output[2];

pipe( Input );
pipe( Output );

if( fork() )
{
    // We're in the parent here.
    // Close the reading end of the input pipe.
    close( Input[ 0 ] );
    // Close the writing end of the output pipe
    close( Output[ 1 ] );

    // Here we can interact with the subprocess.  Write to the subprocesses stdin via Input[ 1 ], and read from the subprocesses stdout via Output[ 0 ].
    ...
}
else
{    // We're in the child here.
     close( Input[ 1 ] );
     dup2( Input[ 0 ], STDIN_FILENO );
     close( Output[ 0 ] );
     dup2( Output[ 1 ], STDOUT_FILENO );

     execlp( "ls", "-la", NULL );
}

Of course, you can replace the execlp with any of the other exec functions as appropriate.

Peter Kovacs
  • 2,657
  • 21
  • 19