I'm currently working on a programming assignment to implement the following in C:
(rev | sort | uniq -c | tee outfile2 | wc) <simple1.txt> outfile1
I've taken a look at many posts regarding this particular assignment, which seems to have been popular in the past. There is a fully fleshed-out solution for it but I'm more interested in doing it in my way, based on the example provided in our textbook. I have successfully been able to pipe the data down to the tee outfile2
command and have run into problems there. However, it doesn't seem to be atomic? I thought data flowing through pipes was supposed to wait on execution until read? As an example I can see the following output when running:
./pipeline -f simple1.txt
aa
aa
aa
ab
ac
ac
ac
ba
ba
bb
ca
cb
cb
cc
cc
cc
db
dc
ev
or
18 18 54
All child processes reaped
.
My code successfully creates outfile2, but it is empty. Either the first or second set of outputs should be in the file, as the output went to the terminal because I included '-'
in the arguments to tee. I recognize that the latter part of the assignment isn't done yet; I'm going step by step.
I appreciate the help on this. My code is below:
/*
* Filename: pipeline.c, read text from stdin to stdout via pipes
* Author: Cody Crawford
* Email: crawfoco@onid.orst.edu
* Course: CS311-400
* Homework: 5
* Citations:
* - TLPI p 900-903
* - Help with pipes, child process, fork, general 'how to' for chaining multiple pipes...
* - My previous code, for getopt() usage
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <getopt.h>
void errExit(char *mystring){
printf("%s\n", mystring);
exit(EXIT_FAILURE);
}
struct command_args{
char out_filename[50];
}mystruct;
struct command_args grab_commands(int argc, char **argv, struct command_args mystruct){
/*
* Grabs command-line arguments. I'm assuming only -f will be passed in with a filename.
*/
char c = getopt(argc, argv, "f");
int end_prog;
if(c == 'f'){
strcat( mystruct.out_filename, argv[2] );
}else{
printf("Error: Unrecognized Input '%c' '%s'\n", c, mystruct.out_filename);
printf("Exiting.\n");
exit(-1);
}
//printf("Received Op: %c && Output File: %s \n", c, mystruct.out_filename);
return mystruct;
}
int main(int argc, char **argv){
struct command_args mystruct;
mystruct = grab_commands(argc, argv, mystruct);
int pfd[2]; /* Pipe file descriptors */
if (pipe(pfd) == -1) /* Create pipe */
errExit("pipe");
/***********************************************************************
* First Child. 'rev' command
***********************************************************************/
switch (fork()) {
case -1:
errExit("fork");
case 0: /* First child: exec 'ls' to write to pipe */
if (close(pfd[0]) == -1) /* Read end is unused */
errExit("close 1");
/* Duplicate stdout on write end of pipe; close duplicated descriptor */
if (pfd[1] != STDOUT_FILENO) { /* Defensive check */
if (dup2(pfd[1], STDOUT_FILENO) == -1)
errExit("dup2 1");
if (close(pfd[1]) == -1)
errExit("close 2");
}
execlp("rev", "rev", mystruct.out_filename, (char *) NULL); /* Writes to pipe */
errExit("execlp rev 1");
default: /* Parent falls through to create next child */
break;
}
/***********************************************************************
* Second Child. 'sort' command
***********************************************************************/
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Second child: exec 'wc' to read from pipe */
if (close(pfd[1]) == -1) /* Write end is unused */
errExit("close 3");
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
errExit("dup2 2");
if (close(pfd[0]) == -1)
errExit("close 4");
}
execlp("sort", "sort", (char *) NULL);
errExit("execlp sort 1");
default: /* Parent falls through */
break;
}
/***********************************************************************
* Third Child. 'uniq -c' command
***********************************************************************/
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Second child: exec 'wc' to read from pipe */
if (close(pfd[1]) == -1) /* Write end is unused */
errExit("close 5");
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
errExit("dup2 3");
if (close(pfd[0]) == -1)
errExit("close 6");
}
sleep(1);
execlp("uniq", "uniq", "-c", (char *) NULL);
errExit("execlp uniq -c 1");
default: /* Parent falls through */
break;
}
/***********************************************************************
* Fourth Child. 'tee outfile2' command
***********************************************************************/
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Second child: exec 'wc' to read from pipe */
if (close(pfd[1]) == -1) /* Write end is unused */
errExit("close 7");
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
errExit("dup2 4");
if (close(pfd[0]) == -1)
errExit("close 8");
}
sleep(5);
execlp("tee", "tee", "outfile2", "-", (char *) NULL);
errExit("execlp tee outfile2 1");
default: /* Parent falls through */
break;
}
/***********************************************************************
* Fifth Child. 'wc' command
***********************************************************************/
switch (fork()) {
case -1:
errExit("fork");
case 0: /* Second child: exec 'wc' to read from pipe */
if (close(pfd[1]) == -1) /* Write end is unused */
errExit("close 9");
/* Duplicate stdin on read end of pipe; close duplicated descriptor */
if (pfd[0] != STDIN_FILENO) { /* Defensive check */
if (dup2(pfd[0], STDIN_FILENO) == -1)
errExit("dup2 5");
if (close(pfd[0]) == -1)
errExit("close 10");
}
execlp("wc", "wc", (char *) NULL);
errExit("execlp wc 1");
default: /* Parent falls through */
break;
}
/* Parent closes unused file descriptors for pipe, and waits for children */
if (close(pfd[0]) == -1)
errExit("close 11");
if (close(pfd[1]) == -1)
errExit("close 12");
while(wait() != -1);
printf("All child processes reaped.\n");
exit(EXIT_SUCCESS);
return;
}