views:

73

answers:

3

I have a program A that needs to send commands to the stdin of a program B and reads back the output of this program B. (Programming in C++, not linux only)

ProgramA -> send letter A -> ProgramB ProgramA <- output of B <- ProgramB

I actually have the first part, send commands to B, working with popen(). I do know that popen is a one way only.

So, what is the best way to do the two way using c++?

A: 

See here for a very closely related question from Jeremy Siek (who works on many boost libraries). Back then (in 2002), it seems the only answer that was pretty close to answering your question could be found in ACE libraries. See here for more information.

Not sure it's the right answer to your question, but it might help.

Benoît
A: 

You may try g_spawn_async_with_pipes from Glib.

This function runs a process and returns its input/output file descriptors.

I use it in my C++ program:

gchar** cmd_argv;
g_shell_parse_argv("your command", &cmd_argc, &cmd_argv, NULL);
g_spawn_async_with_pipes
 (NULL, //Working directory, Inherited from parent (our program)
  cmd_argv,
  NULL, //Null to inherit environment variables from parent
  G_SPAWN_SEARCH_PATH, //Flags
  NULL,
  NULL,
  NULL, //Returns the process ID of child (which runs from our program)
  &stdin_fd, //Standard input (file descriptor)
  &stdout_fd, //Standard output (file descriptor)
  &stderr_fd, //Standard error (file descriptor)
  NULL); //GError*

...

//we have to free cmd_argv
for (int i = 0; i < cmd_argc; ++i) g_free (cmd_argv [i]);

stdin_fd, stdout_fd and stderr_fd are integers (as file descriptors):

gchar* out;
GIOChannel* io = g_io_channel_unix_new(stdout_fd); //g_io_channel_win32_new_fd
while(g_io_channel_read_line(io, &out, NULL, NULL, NULL) == G_IO_STATUS_NORMAL)
{
    //...
    g_free(out);
}

However it's not a C++ library, but may be useful (when you want to write GTK app in C++).

PC2st
+2  A: 

Using posix functionality (so, will work on linux and any system that complies with posix standard), you can use a combination of pipe/execl/dup. In short what happens is:

  • create 2 pipes (one to read and one to write to the child)
  • fork the current process. This keeps open the same fd
  • close the current stdin/stdout. Then use dup (which uses the lowest available descriptor to duplicate what you give to it)
  • execl the child process

Pay attention some flushes are needed. The code of the parent is:

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main ()
{
    pid_t pid;
    int pipe_in[2];   /* This is the pipe with wich we write to the child process. */
    int pipe_out[2];  /* This is the pipe with wich we read from the child process. */

    if (pipe (pipe_in) || pipe (pipe_out)) {
        fprintf (stderr, "Error in creating pipes!\n");
        exit (1);
    }

    /* Attempt to fork and check for errors */
    if ((pid = fork ()) == -1) {
        fprintf (stderr, "Error in fork!\n");
        exit (1);
    }

    if (pid) {
        /* The parent has the non-zero PID. */
        char temp[100];
        int result;
        FILE* child_in;
        FILE* child_out;

        child_in = fdopen(pipe_out[0],"r");
        child_out = fdopen(pipe_in[1],"w");
        close(pipe_out[1]);
        close(pipe_in[0]);

        fprintf(child_out, "something\n"); 

        fgets(temp,100,child_in);
        printf(" Read from child %s \n", temp); 

        /* Send a command to the child. */
        fprintf(child_out, "quit\n"); 
        fflush(child_out);

        fgets(temp,100,child_in);
        printf(" Read from child %s \n", temp);

        wait (&result);             /* Wait for child to finish */
    }
    else {
        /* The child has the zero pid returned by fork*/
        close (1);
        dup (pipe_out[1]);  /* dup uses the lowest numbered unused file descriptor as new descriptor. In our case this now is 1. */

        close (0);          /* dup uses the lowest numbered unused file descriptor as new descriptor. In our case this now is 0. */
        dup (pipe_in[0]);  

        close (pipe_out[0]);
        close (pipe_out[1]);
        close (pipe_in[0]);
        close (pipe_in[1]);
        execl ("child", "child", NULL);
        exit(1); /* Only reached if execl() failed */
    }
    return 0;
}

A simple child is:

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

int main ()
{
    char temp[100];

    do {
        printf ("In child: \n");
        fflush (stdout);
        fgets (temp, 100, stdin);
        printf ("Child read %s\n", temp);
        fflush (stdout);
    } while (!strstr (temp, "quit"));

    return 0;
}

You can compile them with:

gcc -o parent parent.c
gcc -o child child.c
./parent

And you will see

Read from child In child: Read from child Child read quit

vladmihaisima
Better to use `dup2` which allows you to specify the new file descriptor to be used.
dreamlax
Thanks, I could make this work.
Jr. Hames