简体   繁体   English

(在 C 中模拟 UNIX SHELL)如何在 for 循环中实现多个管道?

[英](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.我正在尝试在 C 程序中模拟 unix shell,但它仍处于开始阶段,并且最多只能用于两个管道。 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?我有一个命令向量 (char *com[3][3]),考虑到字符“|”将它们分开,但我的问题是如何在 for 循环中处理更多管道? In the follow the current implementation, I'm trying to execute 3 commands separeted by pipes:在下面的当前实现中,我尝试执行由管道分隔的 3 个命令:

#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?如何在 for 循环中生成 com[n][3]?

"To iterate is human, to recurse is divine" -- Anon. “迭代是人类,递归是神圣的”——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 .要使用它,首先使用如所述设置的commands数组调用它,并inputfd最初-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.如果您想处理<类型重定向,您可能需要在最顶部检查inputfd == -1 ,如果需要进行重定向,并在进入正文的其余部分之前将inputfd替换为适当的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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