0

So I have implemented a multi-pipelined shell in C. Everything seems to work just fine as far as I have test. I built it based off of this Github Example

Now I am trying to implement I/O Redirection inside of my pipelined commands but I am unsure on how to accomplish this task. The book I am reading as well does not go into depth of C implementation of a multi-pipelined shell.

Suppose I want to give a command such as: ls -l | grep something_i_want_to_find | wc > output.txt

and perhaps with appending as well ls -l | grep something_i_want_to_find | wc >> output.txt

My pipeline will fail at the point of the 3rd command when it hits the redirection operator.

I was reading this SO Link as well. One of the comments in the first answer mentions "to redirect to a file, add to the last stage: if (file_fd != 1) { dup2(file_fd, 1); close(file_fd); }" I see where they are wanting to go with this, but I have no clue how I would implement this around my pipeline code.

Here is my code:

Structure to hold command tokens

struct command
{
    char **argv;
};

Pipline Function

void pipeline(struct command *cmds, int numPipes)
{
    pid_t pid;
    int status;
    int fd[2];   // Each pipe has 2 file descriptors
    int i;
    int in = STDIN_FILENO;

    /* Loop for number of pipes */
    for (i = 0; i < numPipes; i++)
    {

        int fd[2];
        pid_t pid;  // Child's pid

        if (pipe(fd) == -1)
        {
            perror("pipe");
            exit(EXIT_FAILURE);
        }
        else if ((pid = fork()) == -1)
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }
        else if (pid == 0)   // Child
        {
            closeFD(fd[0]);
            redirect(in, STDIN_FILENO);
            redirect(fd[1], STDOUT_FILENO);

            execvp(cmds[i].argv[0], (char * const *)cmds[i].argv);
        }
        else    // Parent
        {
            assert(pid > 0);

            close(fd[1]);
            close(in);
            in = fd[0];
        }
    }

    // CODE FROM SO LINK - if (file_fd != 1) { dup2(file_fd, 1); close(file_fd); }

    redirect(in, STDIN_FILENO);
    redirect(STDOUT_FILENO,STDOUT_FILENO);

    for (i = 0; i < numPipes; i++)
    {
        wait(NULL);
    }

    /* Execute the last stage with the current process. */
    execvp (cmds[i].argv[0], (char * const *)cmds[i].argv);
}

Function to close FD's

void closeFD(int fd)
{
    if ((close(fd)) == -1)
    {
        perror("close");
        exit(EXIT_FAILURE);
    }
}

Function to Redirect FD's

void redirect (int oldfd, int newfd)
{
    if (oldfd != newfd)
    {
        if (dup2(oldfd, newfd) != -1)
        {
            if ((close(oldfd)) == -1)
            {
                perror("close");
                exit(EXIT_FAILURE);
            }
        }
        else
        {
            perror("dup2");
            exit(EXIT_FAILURE);
        }
    }
}

My output looks like this when I type in ls -l | grep driver | wc > output.txt

ls -l | grep driver | wc > output.txt                            
Command: ls
Options: -l
Pipe
Command: grep
Arguments: driver
Pipe
Command: wc
File Redirection: >
File: output.txt

wc: >: open: No such file or directory
       1       3      25 output.txt
       1       3      25 total

As you can see, I am recognizing the redirection symbol but this was done in a parser in some different function, which I can provide if need be. The redirection symbol is indeed stored in the struct command **cmds vector. How could I accomplish having I/O redirection inside of my pipeline?

Community
  • 1
  • 1
James Combs
  • 139
  • 11

0 Answers0