If all of the above ramblings seem like a very round-about way of creating and utilizing pipes, there is an alternative.
LIBRARY FUNCTION: popen(); PROTOTYPE: FILE *popen ( char *command, char *type); RETURNS: new file stream on success NULL on unsuccessful fork() or pipe() call NOTES: creates a pipe, and performs fork/exec operations using "command"
While this library function performs quite a bit of the dirty work for you, there is a substantial tradeoff. You lose the fine control you once had by using the pipe() system call, and handling the fork/exec yourself. However, since the Bourne shell is used directly, shell metacharacter expansion (including wildcards) is permissible within the "command" argument.
Pipes which are created with popen() must be closed with pclose(). By now, you have probably realized that popen/pclose share a striking resemblance to the standard file stream I/O functions fopen() and fclose().
LIBRARY FUNCTION: pclose(); PROTOTYPE: int pclose( FILE *stream ); RETURNS: exit status of wait4() call -1 if "stream" is not valid, or if wait4() fails NOTES: waits on the pipe process to terminate, then closes the stream.
Consider this example, which opens up a pipe to the sort command, and proceeds to sort an array of strings:
/***************************************************************************** Excerpt from "Linux Programmer's Guide - Chapter 6" (C)opyright 1994-1995, Scott Burkett ***************************************************************************** MODULE: popen1.c *****************************************************************************/ #include <stdio.h> #define MAXSTRS 5 int main(void) { int cntr; FILE *pipe_fp; char *strings[MAXSTRS] = { "echo", "bravo", "alpha", "charlie", "delta"}; /* Create one way pipe line with call to popen() */ if (( pipe_fp = popen("sort", "w")) == NULL) { perror("popen"); exit(1); } /* Processing loop */ for(cntr=0; cntr<MAXSTRS; cntr++) { fputs(strings[cntr], pipe_fp); fputc('\n', pipe_fp); } /* Close the pipe */ pclose(pipe_fp); return(0); }
Since popen() uses the shell to do its bidding, all shell expansion characters and metacharacters are available for use! In addition, more advanced techniques such as redirection, and even output piping, can be utilized with popen(). Consider the following sample calls:
popen("ls ~scottb", "r"); popen("sort > /tmp/foo", "w"); popen("sort | uniq | more", "w");
As another example of popen(), consider this small program, which opens up two pipes (one to the ls command, the other to sort):
/***************************************************************************** Excerpt from "Linux Programmer's Guide - Chapter 6" (C)opyright 1994-1995, Scott Burkett ***************************************************************************** MODULE: popen2.c *****************************************************************************/ #include <stdio.h> int main(void) { FILE *pipein_fp, *pipeout_fp; char readbuf[80]; /* Create one way pipe line with call to popen() */ if (( pipein_fp = popen("ls", "r")) == NULL) { perror("popen"); exit(1); } /* Create one way pipe line with call to popen() */ if (( pipeout_fp = popen("sort", "w")) == NULL) { perror("popen"); exit(1); } /* Processing loop */ while(fgets(readbuf, 80, pipein_fp)) fputs(readbuf, pipeout_fp); /* Close the pipes */ pclose(pipein_fp); pclose(pipeout_fp); return(0); }
For our final demonstration of popen(), let's create a generic program that opens up a pipeline between a passed command and filename:
/***************************************************************************** Excerpt from "Linux Programmer's Guide - Chapter 6" (C)opyright 1994-1995, Scott Burkett ***************************************************************************** MODULE: popen3.c *****************************************************************************/ #include <stdio.h> int main(int argc, char *argv[]) { FILE *pipe_fp, *infile; char readbuf[80]; if( argc != 3) { fprintf(stderr, "USAGE: popen3 [command] [filename]\n"); exit(1); } /* Open up input file */ if (( infile = fopen(argv[2], "rt")) == NULL) { perror("fopen"); exit(1); } /* Create one way pipe line with call to popen() */ if (( pipe_fp = popen(argv[1], "w")) == NULL) { perror("popen"); exit(1); } /* Processing loop */ do { fgets(readbuf, 80, infile); if(feof(infile)) break; fputs(readbuf, pipe_fp); } while(!feof(infile)); fclose(infile); pclose(pipe_fp); return(0); }
Try this program out, with the following invocations:
popen3 sort popen3.c popen3 cat popen3.c popen3 more popen3.c popen3 cat popen3.c | grep main