11.3 Pipelines

Team-FLY

Pipelines, introduced in Section 6.2, connect filters in an assembly line to perform more complicated functions.

Example 11.12

The following command redirects the output of ls -l to the standard input of sort and the standard output of sort to the file temp .

 ls -l  sort -n +4 > temp 

The ls and the sort commands are distinct processes connected in a pipeline. The connection does not imply that the processes share file descriptors, but rather that the shell creates an intervening pipe to act as a buffer between them.

Program 11.5 parseandredirect.c

Functions to handle redirection of standard input and standard output. These functions must be called in a particular order. The redirection that occurs last must be handled first .

 #include <errno.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #define FFLAG (O_WRONLY  O_CREAT  O_TRUNC) #define FMODE (S_IRUSR  S_IWUSR) int parseandredirectin(char *cmd) {    /* redirect standard input if '<' */     int error;     int infd;     char *infile;     if ((infile = strchr(cmd, '<')) == NULL)         return 0;     *infile = 0;                  /* take everything after '<' out of cmd */     infile = strtok(infile + 1, " \t");     if (infile == NULL)         return 0;     if ((infd = open(infile, O_RDONLY)) == -1)         return -1;     if (dup2(infd, STDIN_FILENO) == -1) {         error = errno;                       /* make sure errno is correct */         close(infd);         errno = error;         return -1;     }     return close(infd); } int parseandredirectout(char *cmd) {  /* redirect standard output if '>' */     int error;     int outfd;     char *outfile;     if ((outfile = strchr(cmd, '>')) == NULL)         return 0;     *outfile = 0;                  /* take everything after '>' out of cmd */     outfile = strtok(outfile + 1, " \t");     if (outfile == NULL)         return 0;     if ((outfd = open(outfile, FFLAG, FMODE)) == -1)         return -1;     if (dup2(outfd, STDOUT_FILENO) == -1) {         error = errno;                        /* make sure errno is correct */         close(outfd);         errno = error;         return -1;     }     return close(outfd); } 

Program 11.6 contains a version of executecmd that handles a pipeline of arbitrary length. The implementation uses makeargv with the pipeline symbol as a delimiter to make an array of commands for the pipeline. For each command (except the last), executecmd creates a pipe and a child process. The executecmd redirects the standard output of each command, except the last through a pipe, to the standard input of the next one. The parent redirects its standard output to the pipe and executes the command by calling executeredirect of Program 11.7. The child redirects its standard input to come from the pipe and goes back to the loop to create a child to handle the next command in the pipeline. For the last command in the list, executecmd does not create a child or pipe but directly calls executeredirect .

Errors need to be handled very carefully . Program 11.6 creates multiple child processes. This version of executecmd never returns. An error in any of the processes results in a call to perror_exit , which prints an appropriate message to standard error and exits.

The executeredirect function takes three parameters: the command string and two flags. If the first flag is nonzero, executeredirect allows standard input to be redirected. If the second flag is nonzero, executeredirect allows standard output to be redirected. The pipeline can redirect standard input only for the first command in the pipeline and can redirect standard output only for the last one.

The executecmd function only sets the first flag parameter of executeredirect for the call with i equals 0. The executecmd only sets the second flag after the last loop iteration completes. If the pipeline contains only one command (no pipeline symbol on the command line), executecmd does not execute the loop body and calls executeredirect with both flags set. In this case, executeredirect behaves similarly to the executecmd in executecmdredirect (Program 11.4).

The first if in executeredirect handles the case of the output redirection occurring before the input redirection, as discussed in Exercise 11.10.

Exercise 11.13

What would this shell do with the following command.

 ls -l > temp1  sort -n +4 > temp 

Answer:

The redirection of standard output to temp1 would be ignored. The shell would treat > and temp1 as names of files to list. Most real shells would detect this as an error.

Exercise 11.14

How are the processes in the following pipeline related when they are executed by executecmdpipe ?

 ls -l  sort -n +4  more 

Answer:

The first command, ls -l , is a child of the shell. The second command, sort -n +4 , is a child of ls . The third command, more , is a child of sort .

Program 11.6 executecmdpipe.c

The executecmd function that handles pipelines .

 #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> void executeredirect(char *s, int in, int out); int makeargv(const char *s, const char *delimiters, char ***argvp); static void perror_exit(char *s) {     perror(s);     exit(1); } void executecmd(char *cmds) {     int child;     int count;     int fds[2];     int i;     char **pipelist;     count = makeargv(cmds, "", &pipelist);     if (count <= 0) {         fprintf(stderr, "Failed to find any commands\n");         exit(1);     }     for (i = 0; i < count - 1; i++) {              /* handle all but last one */         if (pipe(fds) == -1)             perror_exit("Failed to create pipes");         else if ((child = fork()) == -1)             perror_exit("Failed to create process to run command");         else if (child) {                                       /* parent code */             if (dup2(fds[1], STDOUT_FILENO) == -1)                 perror_exit("Failed to connect pipeline");             if (close(fds[0])  close(fds[1]))                 perror_exit("Failed to close needed files");             executeredirect(pipelist[i], i==0, 0);             exit(1);         }         if (dup2(fds[0], STDIN_FILENO) == -1)                    /* child code */             perror_exit("Failed to connect last component");         if (close(fds[0])  close(fds[1]))             perror_exit("Failed to do final close");     }     executeredirect(pipelist[i], i==0, 1);             /* handle the last one */     exit(1); } 
Program 11.7 executeredirect.c

A function to handle a single command with possible redirection .

 #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int makeargv(const char *s, const char *delimiters, char ***argvp); int parseandredirectin(char *s); int parseandredirectout(char *s); void executeredirect(char *s, int in, int out) {     char **chargv;     char *pin;     char *pout;     if (in && ((pin = strchr(s, '<')) != NULL) &&           out && ((pout = strchr(s, '>')) != NULL) && (pin > pout) ) {         if (parseandredirectin(s) == -1) { /* redirect input is last on line */             perror("Failed to redirect input");             return;         }         in = 0;     }     if (out && (parseandredirectout(s) == -1))         perror("Failed to redirect output");     else if (in && (parseandredirectin(s) == -1))         perror("Failed to redirect input");     else if (makeargv(s, " \t", &chargv) <= 0)         fprintf(stderr,"Failed to parse command line\n");     else {         execvp(chargv[0], chargv);         perror("Failed to execute command");     }     exit(1); } 
Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net