zarth@drutx.UUCP (CovartDL) (12/21/84)
I am interested in using the popen() command to form a pipe between my C program and the UNIX sort command. I would appreciate any information on the best way to do this(examples would help). I should point out that the input to the sort command would be comming from an array and the output should be stored in an array. Thanks in advance, Zarth Arn (alias Dave Covart) drutx!zarth M E R R Y C H R I S T M A S A N D A H A P P Y N E W Y E A R !!!!
smh@mit-eddie.UUCP (Steven M. Haflich) (12/26/84)
>> I am interested in using the popen() command to form a pipe >> between my C program and the UNIX sort command. I would appreciate >> any information on the best way to do this(examples would help). >> I should point out that the input to the sort command would be >> comming from an array and the output should be stored in an array. > >It is very hard to manage both ends of a piped filter from a program. > >Have you considered using the qsort() library routine to sort your data? It is very hard to connect both ends of a piped filter from a single program in the general case, but sort(I) is a special case in that it is guaranteed not to produce output until it reads the EOF on its input. Something like the following (untested) ought to work. It would be nicer if it error checked the pipe, fork, wait, and fdopen calls: int pipe_to[2]; int pipe_fr[2]; FILE sortfile; ... pipe(pipe_to); pipe(pipe_from); if (fork()==0) { close(pipe_to[1]); close(pipe_from[0]); close(0); close(1); dup(pipe_to[0]); close(pipe_to[0]); dup(pipe_fr[1]); close(pipe_fr[1]); execl("/bin/sort","sort" /* optional comma-separated sort args */ , 0); perror("Can't exec /bin/sort"); exit(-1); } close(pipe_to[0]); close(pipe_fr[1]); sortfile = fdopen(pipe_to[1],"w"); /* Code to write data to the sort program goes here, e.g.: for (i=0; i<arraydim; i++) fprintf(sortfile,"%d\n",array[i]); */ fclose(sortfile); sortfile = fdopen(pipe_from[0],"r"); /* Code to read data back from the sort goes here, e.g.: Note that this code doesn't make use of the fact that sort necessarily returns the same number of items as written. for (i=0; fscanf(sortfile,"%d\n",&array[i]) == 1; i++); */ fclose(sortfile); wait(0); Hope this helps. It isn't nearly so confusing as it looks, provided I typed it extempore without errors.
gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (12/27/84)
> I am interested in using the popen() command to form a pipe > between my C program and the UNIX sort command. I would appreciate > any information on the best way to do this(examples would help). > I should point out that the input to the sort command would be > comming from an array and the output should be stored in an array. It is very hard to manage both ends of a piped filter from a program. Have you considered using the qsort() library routine to sort your data?
jack@vu44.UUCP (Jack Jansen) (12/27/84)
> I am interested in using the popen() command to form a pipe > between my C program and the UNIX sort command. I would appreciate > any information on the best way to do this(examples would help). > I should point out that the input to the sort command would be > comming from an array and the output should be stored in an array. It is very hard to get *both* ends of a pipe into your hands. The simplest solution is to use a tempfile, which makes your code something like char command[BUFSIZ], *tnm; FILE *tfp; tnm = tempname(); /* Get temp filename */ if((tfp = fopen(tnm,"w"))==NULL) <error handling>; write_array_to_file(data,tfp); fclose(tfp); sprintf(command,"sort %s",tnm); if( (tfp=popen(command,"r")) == NULL ) <error handling>; read_array_from_file(data,tfp); if(pclose(tfp) != 0 ) <error handling>; In this case, read_array_from_file() and write_array_to_file() read/write an array to a file (in ASCII), probably with a printf or scanf loop. If you don't want to use a tempfile, you have to do it yourself. This means setting up two pipes, forking, re-directing all file- descriptors, etc etc etc. -- Jack Jansen, {seismo|philabs|decvax}!mcvax!vu44!jack or ...!vu44!htsa!jack If *this* is my opinion, I wasn't sober at the time.
geoff@desint.UUCP (Geoff Kuenning) (12/29/84)
*** ****** *** *** If you follow this article up, PLEASE remove net.sources from the list *** *** ****** *** In article <6770@brl-tgr.ARPA> gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) writes: >It is very hard to manage both ends of a piped filter from a program. > >Have you considered using the qsort() library routine to sort your data? Doug's second point seems hard to argue. Nevertheless, there are applications besides sorting that require being able to pipe an array through a program and back into that same array. Vi, provides this feature, for example. Sort is easier than most, because it buffers its output and thus cannot hang a pipe. The following example is extracted from a program I wrote that provides this feature. I have removed a lot of clutter, which has the unfortunate side effect that I may have broken it in the process (I have collapsed some subroutines whose division have nothing to do with the problem at hand, and I may have blown the argument substitution). Don't forget to remove my .signature! -----------------------------------cut here------------------------------- #include <signal.h> /* * pipethru will pipe the "n" bytes in "ibuf" through "command". The result * is placed in "obuf," but is limited to "lim" bytes. The return value is * the number of bytes read, or -1 if an error occurs. * * "ibuf" and "obuf" may overlap in any fashion without difficulty. */ int pipethru (command, ibuf, n, obuf, lim) char * command; char * ibuf; int n; char * obuf; int lim; { int fd; /* Loop counter for closing files */ int inpipes[2]; /* Descriptors for the input pipe */ int outpipes[2]; /* Descriptors for the output pipe */ int pipe_pid; /* Pid of the process we pipe thru */ char * shellname; /* Shell to use to run "command" */ if (pipe (inpipes) < 0) /* Set up the pipes we will use */ return -1; if (pipe (outpipes) < 0) { close (inpipes[0]); close (inpipes[1]); return -1; } /* (You may want to change the following code to retry if the error * is EAGAIN. Also, bsd systems should use vfork here (but *not* in * handlepipe, below */ if ((pipe_pid = fork ()) < 0) /* Make a kid - quit if can't */ { close (inpipes[0]); close (inpipes[1]); close (outpipes[0]); close (outpipes[1]); return -1; } else if (pipe_pid > 0) /* Parent */ { close (outpipes[0]); /* Close unused ends of pipes */ close (inpipes[1]); return handlepipe (pipe_pid, inpipes[0], outpipes[1], ibuf, n, obuf, lim); } else /* Child */ { close (0); /* Set up stdin as read end of the */ dup (outpipes[0]); /* ..output pipe */ close (outpipes[0]); close (outpipes[1]); /* Close unused end of pipe */ close (1); /* Set up stdout as write end of the */ dup (inpipes[1]); /* ..input pipe */ close (inpipes[0]); close (inpipes[1]); for (fd = 3; fd < 20; ) /* Close all others except stderr */ close (fd++); /* At this point, standard input is the data coming from the buffer, and standard output is the data that will go back. All we need to do is exec the command in question. */ if ((shellname = getenv ("SHELL")) == NULL) shellname = "/bin/sh"; execl (shellname, shellname, "-c", command, (char *) NULL); exit (127); } } /* * handlepipe is called once we have the command running at the end of a pair * of pipes. Handlepipe will do the real work, writing "buf" to the output * pipe and reading the result into the input pipe. To avoid hanging due to * full pipes, we fork into two processes. The child will write, and the * parent will read. */ int handlepipe (pipe_pid, inpipe, outpipe, ibuf, n, obuf, lim) int pipe_pid; /* Pid of the process we pipe thru */ int inpipe; /* Pipe we read from */ int outpipe; /* Pipe we write to */ char * ibuf; int n; char * obuf; int lim; { int exit_pid; /* Pid of last child to exit */ int exit_stat; /* Return status of child */ int (*saveint) (); /* Places to save some signals */ int (*savequit) (); int write_pid; /* Pid of child who is writing */ saveint = signal (SIGINT, SIG_IGN); /* Ignore signals the command sees */ savequit = signal (SIGQUIT, SIG_IGN); /* Don't use vfork here! */ if ((write_pid = fork ()) < 0) /* Make a kid to write */ { /* Failure! */ close (outpipe); /* Clean up pipes */ close (inpipe); /* ..this also kills the command */ signal (SIGINT, saveint); /* Put the signals back */ signal (SIGQUIT, savequit); while ((exit_pid = wait ((int *) NULL)) != pipe_pid && exit_pid > 0) ; return -1; } else if (write_pid == 0) /* Child */ { close (inpipe); /* Close unused pipe */ exit_stat = (write (outpipe, buf, n) == n) ? 0 : 1; close (outpipe); exit (exit_stat); } else if (write_pid > 0) /* Parent? */ { close (outpipe); /* Close unused pipe */ n = read (inpipe, buf, lim); /* Read back the command output */ close (inpipe); signal (SIGINT, saveint); /* Put the signals back */ signal (SIGQUIT, savequit); /* * The only thing left now is to wait for both children to exit. */ while ((pipe_pid > 0 || write_pid > 0) && (exit_pid = wait (&exit_stat)) > 0) { if (exit_pid == pipe_pid) pipe_pid = exit_stat ? -1 : 0; else if (exit_pid == write_pid) write_pid = exit_stat ? -1 : 0; } if (write_pid < 0 /* This means you can't write pipe */ || pipe_pid < 0) /* This means command failed */ return -1; else return n; /* All is well, return no. read */ } } -----------------------------cut again------------------------------- -- Geoff Kuenning ...!ihnp4!trwrb!desint!geoff