【问题标题】:(SIMULATING A UNIX SHELL IN C) How to implement multiple pipes in a for loop?(在 C 中模拟 UNIX 外壳)如何在 for 循环中实现多个管道?
【发布时间】:2019-08-17 04:04:27
【问题描述】:

我正在尝试在 C 程序中模拟 unix shell,但它仍处于开始阶段,最多可用于两个管道。我有一个命令向量 (char *com[3][3]),考虑到字符“|”,它们是分开的,但我的问题是如何在 for 循环中继续处理更多管道?在遵循当前的实现中,我正在尝试执行由管道分隔的 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;
}

如何在 for 循环中生成 com[n][3]?

【问题讨论】:

    标签: c unix operating-system pipe


    【解决方案1】:

    “迭代是人,递归是神”——匿名。

    我会用递归的方法来解决这个问题。这是非常罕见的情况之一,成为Three Star programmer 几乎是合理的。 ;)

    这是完全未经测试的,但应该能让你指向正确的方向。

    // 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;
        }
    }
    

    要使用它,首先调用它并按照描述设置commands 数组,然后inputfd 最初调用-1

    如果您想处理&lt; 类型的重定向,您可能需要检查最顶部的inputfd == -1,如果需要进行重定向,并在输入正文的其余部分之前将inputfd 替换为适当的值。

    【讨论】:

      猜你喜欢
      • 2022-10-16
      • 2011-04-25
      • 2020-10-06
      • 1970-01-01
      • 2022-01-23
      • 2014-02-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多