1

How do I call an external program in C, such that I can write to its stdin and read from its stdout? There are a lot of questions like this one but none seem to fully answer it. Say for example, that I have some program in which I want some other program to do something, like this pseudocode:

int * myinput = "this is my input"
FILE ** io = external_program("otherprogram arg1 arg2");
char c;
fprintf(io[0],myinput); // Writes my input to the programs stdin
while((c = fgetc(io[1])) != EOF) {
    printf("I just read %c from otherprogram!",c);
}

As I can't find it I'd assume something like external_program may not exist. How could I get something like it though, where I have a program running externally with filedescriptors for its stdin, stdout, if possible also stderr?

This way I could, for example, have some program that reads code in some language, compile it to C or some other language, and use GCC to get the result, without having to create (and possibly overwrite) files, like so:

FILE ** io = external_program("gcc -xc -");
FILE * output = open(output_file_path); // Program's own output

// ... generate C code, writing to io[0] ...

// Now write the output to our own output file
while((c = fgetc(io[1])) != EOF) {
    fputs(output,c);
}
close (output);
Lara
  • 2,594
  • 4
  • 24
  • 36

3 Answers3

1

You need two pipes, one for piping input into the child's stdin the other for reading it's stdout. In a UNIX/Linux like environment, creating the child process could look like this:

FILE    **p2open( const char *cmd, FILE **fpbuf )
{
    FILE    **result;
    int     fd1[2], fd2[2];
    int     ispipe1 = ERROR, ispipe2 = ERROR;
    pid_t   cpid;

    // open two pipes: Pipe1: parent reads, pipe2_: paretn writes

    if( (ispipe1 = pipe( fd1 )) == -1 ||
        (ispipe2 = pipe( fd2 )) == -1   )
    {
            return NULL;
    }


    switch( cpid = fork() ) {
    case -1:
            return NULL;


    case 0:                        /* Child                   */
            dup2( fd2[0], 0 );
            dup2( fd1[1], 1 );
            close( fd1[0] ); close( fd1[1] );
            close( fd2[0] ); close( fd2[1] );

            execlp( "/bin/sh", "sh", "-c", cmd, NULL );
            break;

    default:                        /* parent                       */
            if( (fpbuf[0] = fdopen( fd1[0], "r" )) == NULL ||
                (fpbuf[1] = fdopen( fd2[1], "w" )) == NULL   )
            {
                    result = NULL;
            } else {
                    result = fpbuf;
            }
    }

    if( ispipe1 != ERROR ) {
            close( fd1[1] );
            if( !result )           close( fd1[0] );
    }
    if( ispipe2 != ERROR ) {
            close( fd2[0] );
            if( !result )           close( fd2[1] );
    }
    return result;
}

You can now call:

 FILE *pipefp[2];

 if( p2open( "mycmd", pipefp ) != NULL ) {
      // now you have pipefp[0] for readinf pipefp[1] for writing
      ..
 }

Of course you should also implement a matching p2close() function, but I think you can manage that

This is a test main that works for me on Linux and solaris:

int main(int argc, char *argv[] )
{
    FILE    *fp[2];
    int     c;

    if( p2open( "cat", fp ) != NULL ) {
            fputs( "go!\n", fp[1] );
            fclose( fp[1] );
            while( (c = fgetc( fp[0] )) != EOF ) {
                    fputc( c, stdout );
            }
    }

}
Ingo Leonhardt
  • 9,435
  • 2
  • 24
  • 33
0

This goes beyond vanilla C and into interprocess communications, which is platform-specific (that is, the answer for Windows may be different from the answer for *nix).

Here's a simple *nix example using a pipe (taken from the now out-of-print "Practical Unix Programming" by Steve and Kay Robbins):

/**
 * The following includes are specific to *nix
 */
#include <unistd.h>
#include <fcntl.h>

int main( int argc, char **argv )
{
  int fd[2]; // file descriptors for pipe
  pid_t childpid;

  pipe( fd );

  if ( (childpid = fork()) == 0 )
  {
    // this is the child process
    dup2( fd[1], STDOUT_FILENO );
    close( fd[0] );
    close( fd[1] );
    execl( "/bin/ls", "ls", "-l", argv[1], NULL );
    perror( "The exec of ls failed" );
  }
  else
  {
    // this is the parent process
    dup2( fd[0], STDIN_FILENO );
    close( fd[0] );
    close( fd[1] );
    execl( "/usr/bin/sort", "sort", "-n", "+4", NULL );
    perror( "The exec of sort failed" );
  }

  return 0;
}

This is basically a C implementation of the shell command ls -l <path> | sort -n +4. The standard output of ls is fed into the standard input of sort using the fd descriptor table and the pipe command.

As written, this is a one-way communications channel, which is all I've ever done. I don't have a good two-way example handy.

John Bode
  • 119,563
  • 19
  • 122
  • 198
-1
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void main()
{
    char ext_prog[]="prog name"; //your external program
    char arguments[]="args > file.txt"; //arguments
    strcat(ext_prog,arguments); //join the strings to ext_prog
    system(ext_prog); //execute on shell
}

This will save the output to file.txt which you can be parsed.

EDIT: There are other ways to go about this. This came of the top of mind. Try using popen() and FILE pointer.

  • This doesn't answer the question at all - there is no means to allow the program to send data to/from the calling executable via it's stdin or stdout. – Chris Turner Feb 28 '17 at 15:34
  • My question was specifically how to write to its `stdin` and read its `stdout`. Maybe I'm too dumb but I don't see how this code allows me to do that. – Lara Feb 28 '17 at 15:34
  • You can get the input required by the program using scanf/gets and then pass it to system call. Like I said, there are other ways to go about this and this struck me first. – Prajjwal Srivastav Feb 28 '17 at 15:37