5

I'm using the execl function to run a Linux process from C. When I do, for example:

int cmd_quem() { 
  int result; 
  result = fork();
  if(result < 0) {
    exit(-1);
  }

  if (result == 0) {
    execl("/usr/bin/who", "who", NULL);
    sleep(4); //checking if father is being polite 
    exit(1); 
  } 
  else { 
    // father's time
    wait();
  }

  return 0;
}

I get on the console the result of doing "who" on the terminal. What I'd like to know is if there is any function to "catch" the output result from a command. What I mean is, if there is anyway to catch this:

feuplive tty5         2009-11-21 18:20

Which is one of the lines resulting from the who command.

out_sider
  • 51
  • 1
  • 2

3 Answers3

6

To do this, you need to open a pipe. You then replace the child's stdout with the writing end of the pipe, and read from the reading end of the pipe in the parent. Like this modified version of your code:

int cmd_quem(void) {
  int result;
  int pipefd[2];
  FILE *cmd_output;
  char buf[1024];
  int status;

  result = pipe(pipefd);
  if (result < 0) {
    perror("pipe");
    exit(-1);
  }

  result = fork();
  if(result < 0) {
    exit(-1);
  }

  if (result == 0) {
    dup2(pipefd[1], STDOUT_FILENO); /* Duplicate writing end to stdout */
    close(pipefd[0]);
    close(pipefd[1]);

    execl("/usr/bin/who", "who", NULL);
    _exit(1);
  }

  /* Parent process */
  close(pipefd[1]); /* Close writing end of pipe */

  cmd_output = fdopen(pipefd[0], "r");

  if (fgets(buf, sizeof buf, cmd_output)) {
    printf("Data from who command: %s\n", buf);
  } else {
    printf("No data received.\n");
  }

  wait(&status);
  printf("Child exit status = %d\n", status);

  return 0;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
caf
  • 233,326
  • 40
  • 323
  • 462
  • Good job on cleaning up the plumbing correctly - so few get it right. – Jonathan Leffler Nov 22 '09 at 19:15
  • i did that BUT it only worked completly when i switched the line `wait()` and placed it before `fgets()`, any explanation ? – mf_ Aug 12 '13 at 16:18
  • @mf_: That should not make any difference - what OS are you using? – caf Aug 12 '13 at 21:55
  • @caf ubuntu 10.04LTS, but makes some sense now, the father catches whats is in the buffer even if just a couple of bytes, while if we wait() for the son to finish completly the buffer will have it all, this is my interpretation, if u want i can post my tweaked version of this piece – mf_ Sep 06 '13 at 09:02
  • @mf_: `fgets()` should read until a newline or end-of-file, it should not just read "a couple of bytes" if the child hasn't output a newline or exited. – caf Sep 06 '13 at 13:24
  • @caf this was the line `execl("/bin/ping","ping","stackoverflow.com","-c 1 -s 1",NULL);` i also replaced `fgets()` with a `read()` – mf_ Sep 09 '13 at 18:02
  • @mf_: That's the problem - `read()` does not buffer, that's why I used `fdopen()` and `fgets()`. – caf Sep 09 '13 at 21:42
5

First, execl does not return unless there's a problem like the executable is not found. That sleep(4) is probably never executed.

As for redirecting and getting the output, check out the Unix Programming FAQ. Look for spawn_background_command.

Gonzalo
  • 20,805
  • 3
  • 75
  • 78
3

The exec() family of functions creates a new process image from a regular, executable file. This file is either an executable object file, or an interpreter script. There is no return from a successful call to an exec() function, because the calling process is functionally replaced by the new process.

So any code after exec() is never executed unless it is failed.

If you want to capture output of a shell command you need popen.

sud03r
  • 19,109
  • 16
  • 77
  • 96