11.3 Pipelines 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.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); }
|