简体   繁体   中英

(SIMULATING A UNIX SHELL IN C) How to implement multiple pipes in a for loop?

I'm trying to simulate a unix shell in a C program and it's still in the beginning and working for at most two pipes. I have a vector of commands (char *com[3][3]), which were separated considering the character "|", but my question is how to proceed to more pipes in a for loop? In the follow the current implementation, I'm trying to execute 3 commands separeted by pipes:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>

int main(int argc, char **argv){

    //Vector with positions of pipes found, position 0 reserved for the total amount of commands.

    char* com[3][3] = { { "/bin/ls", "-la", 0 },
        { "/bin/grep", ".", 0}, { "/usr/bin/wc", "-l", 0 }};

    //EXECUTE COMMANDS
    pid_t fork1, fork2, fork3;
    int fd1[2], fd2[2];

    if(pipe(fd1) < 0){
        perror("pipe1");
    }
    if(pipe(fd2) < 0){
        perror("pipe2");
    }



    //COMMAND 1
    fork1 = fork();
    if(fork1 == 0){
        dup2(fd1[1], STDOUT_FILENO);
        close(fd1[0]);
        close(fd2[0]);
        close(fd2[1]);
        execvp(com[0][0], com[0]);
        perror("execvp 1");
        exit(EXIT_FAILURE);
    }

    //COMMAND 2
    fork2 = fork();
    if(fork2 == 0){
        dup2(fd1[0], STDIN_FILENO);
        dup2(fd2[1], STDOUT_FILENO);
        close(fd1[1]);
        close(fd2[0]);
        execvp(com[1][0], com[1]);
        perror("execvp 2");
        exit(EXIT_FAILURE);
    }

    //COMMAND 3
    fork3 = fork();
    if(fork3 == 0){
        dup2(fd2[0], STDIN_FILENO);
        close(fd2[1]);
        close(fd1[0]);
        close(fd1[1]);
        execvp(com[2][0], com[2]);
        perror("execvp 3");
        exit(EXIT_FAILURE);
    }

    close(fd1[0]);
    close(fd1[1]);
    close(fd2[0]);
    close(fd2[1]);

    waitpid(-1, NULL, 0);
    waitpid(-1, NULL, 0);
    waitpid(-1, NULL, 0);

    return 0;
}

How do I make to com[n][3], in a for loop?

"To iterate is human, to recurse is divine" -- Anon.

I'd attack this with a recursive approach. This is one of those very rare occasions when being a Three Star programmer is almost justified. ;)

This is completely untested, but should get you pointed in the correct direction.

// You'll need to rearrange your command strings into this three dimensional array
// of pointers, but by doing so you allow an arbitrary number of commands, each with
// an arbitrary number of arguments.
int executePipe(char ***commands, int inputfd)
{
    // commands is NULL terminated
    if (commands[1] == NULL)
    {
        // If we get here there's no further commands to execute, so run the 
        // current one, and send its result back.
        pid_t pid;
        int status;
        if ((pid = fork()) == 0)
        {
            // Set up stdin for this process.  Leave stdout alone so output goes to the
            // terminal.  If you want '>' / '>>' redirection to work, you'd do that here
            if (inputfd != -1)
            {
                dup2(inputfd, STDIN_FILENO);
                close(inputfd);
            }
            execvp(commands[0][0], commands[0]);
            perror("execvp");
            exit(EXIT_FAILURE);
        }
        else if (pid < 0)
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }
        waitpid(pid, &status, 0);
        return status;
    }
    else
    {
        // Somewhat similar to the above, except we also redirect stdout for the
        // next process in the chain
        int fds[2];
        if (pipe(fds) != 0)
        {
            perror("pipe");
            exit(EXIT_FAILURE);
        }
        pid_t pid;
        int status;
        if ((pid = fork()) == 0)
        {
            // Redirect stdin if needed
            if (inputfd != -1)
            {
                dup2(inputfd, STDIN_FILENO);
                close(inputfd);
            }
            dup2(fds[1], STDOUT_FILENO);
            close(fds[1]);
            execvp(commands[0][0], commands[0]);
            perror("execvp");
            exit(EXIT_FAILURE);
        }
        else if (pid < 0)
        {
            perror("fork");
            exit(EXIT_FAILURE);
        }
        // This is where we handle piped commands.  We've just executed
        // commands[0], and we know there's another command in the chain.
        // We have everything needed to execute that next command, so call
        // ourselves recursively to do the heavy lifting.
        status = executePipe(++commands, fds[0]);
        // As written, this returns the exit status of the very last command
        // in the chain.  If you pass &status as the second parameter here
        // to waitpid, you'll get the exit status of the first command.
        // It is left as an exercise to the reader to figure how to get the
        // the complete list of exit statuses
        waitpid(pid, NULL, 0);
        return status;
    }
}

To use this, call it initially with the commands array set up as described, and inputfd initially -1 .

If you want to handle < type redirection, you probably want to check for inputfd == -1 at the very top, do redirection if requested and replace inputfd with the appropriate value before entering the remainder of the body.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM