質問

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;
}
役に立ちましたか?

解決

You've only created one pipe, yet the command line needs four pipes. That means that the commands after the first are fighting over the output from the first, and basically nothing works sanely.

You'll need to create more pipes. You don't have to create them all up front, but you do need to keep an eye on making sure they're all closed.

Also, you're implementing a subtly different pipeline from the one claimed:

rev simple1.txt | sort | uniq -c | tee outfile2 | wc

Your program doesn't seem to be redirecting the output of wc (which doesn't matter; the shell is quite good at doing that for you).

And another also: your usage of getopt() is extremely unorthodox.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top