Connecting n commands with pipes in a shell? Connecting n commands with pipes in a shell? shell shell

Connecting n commands with pipes in a shell?


Nothing complex here, just have in mind that the last command should output to the original process' file descriptor 1 and the first should read from original process file descriptor 0. You just spawn the processes in order, carrying along the input side of the previous pipe call.

So, here's are the types:

#include <unistd.h>struct command{  const char **argv;};

Make a helper function with a simple well defined semantics:

intspawn_proc (int in, int out, struct command *cmd){  pid_t pid;  if ((pid = fork ()) == 0)    {      if (in != 0)        {          dup2 (in, 0);          close (in);        }      if (out != 1)        {          dup2 (out, 1);          close (out);        }      return execvp (cmd->argv [0], (char * const *)cmd->argv);    }  return pid;}

And here's the main fork routine:

intfork_pipes (int n, struct command *cmd){  int i;  pid_t pid;  int in, fd [2];  /* The first process should get its input from the original file descriptor 0.  */  in = 0;  /* Note the loop bound, we spawn here all, but the last stage of the pipeline.  */  for (i = 0; i < n - 1; ++i)    {      pipe (fd);      /* f [1] is the write end of the pipe, we carry `in` from the prev iteration.  */      spawn_proc (in, fd [1], cmd + i);      /* No need for the write end of the pipe, the child will write here.  */      close (fd [1]);      /* Keep the read end of the pipe, the next child will read from there.  */      in = fd [0];    }  /* Last stage of the pipeline - set stdin be the read end of the previous pipe     and output to the original file descriptor 1. */    if (in != 0)    dup2 (in, 0);  /* Execute the last stage with the current process. */  return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);}

And a small test:

intmain (){  const char *ls[] = { "ls", "-l", 0 };  const char *awk[] = { "awk", "{print $1}", 0 };  const char *sort[] = { "sort", 0 };  const char *uniq[] = { "uniq", 0 };  struct command cmd [] = { {ls}, {awk}, {sort}, {uniq} };  return fork_pipes (4, cmd);}

Appears to work. :)


First, you are prematurely closing the pipes. Close only the end that you don't need in the current process, and remember to close stdin/stdout in the child.

Secondly, you need to remember the fd from the previous command. So, for two processes, this looks like:

int pipe[2];pipe(pipe);if ( fork() == 0 ) {     /* Redirect output of process into pipe */     close(stdout);     close(pipe[0]);     dup2( pipe[1], stdout );     execvp(commands[0].argv[0], &commands[0].argv[0]);} if ( fork() == 0 ) {     /* Redirect input of process out of pipe */     close(stdin);     close(pipe[1]);     dup2( pipe[0], stdin );     execvp(commands[1].argv[0], &commands[1].argv[0]);}/* Main process */close( pipe[0] );close( pipe[1] );waitpid();

Now your job is to add error handling to this and generate n-1 pipes for n processes to start. The code in the first fork() block needs to be run for the appropriate pipe for processes 1..n-1, and the code in the second fork() block for the processes 2..n.