使用系统调用fork()的多个管道实现execvp()wait()pipe() – 它根本不起作用

我需要实现处理多个管道命令的shell.例如,我需要能够处理这个:ls | grep -i cs340 |排序| uniq | cut -c 5.我假设问题是我没有将前一个命令的输出传递给下一个命令的输入.
当我执行我的代码时,它没有给我输出.我正在使用这个伪代码:

for cmd in cmds
    if there is a next cmd
        pipe(new_fds)
    fork
    if child
        if there is a previous cmd
            dup2(old_fds[0], 0)
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            close(new_fds[0])
            dup2(new_fds[1], 1)
            close(new_fds[1])
        exec cmd || die
    else
        if there is a previous cmd
            close(old_fds[0])
            close(old_fds[1])
        if there is a next cmd
            old_fds = new_fds
if there are multiple cmds
    close(old_fds[0])
    close(old_fds[1])

以下是处理多个管道的函数的源代码.

void execute_multiple_commands(struct command ** commands_to_exec,
        int num_commands_p)
{
    pid_t status;
    int i, err;
    int new_fd[2], old_fd[2];
    pid_t pid, cpid;

    // creating child process
    if ( (cpid = fork()) == -1)
    {
        fprintf(stderr, "Could not create child process, exiting...");
        exit(1);
    }

    if (cpid == 0) // in the child process we run multiple pipe handling
    {
        for (i = 0; i < num_commands_p; i++) // for each cmd in cmds
        {
            if (i+1 < num_commands_p) // if there is next cmd
                pipe(new_fd);

            if ( (pid = fork()) == -1)
            {
                fprintf(stderr, "Could not create child process, exiting...");
                exit(1);
            }

            if (pid == 0) // if child
            {
                if (i != 0) // if there is a previous command
                {
                    dup2(old_fd[0], 0); // setting up old_pipe to input into the child
                    close(old_fd[0]);
                    close(old_fd[1]);

                }
                if (i+1 < num_commands_p) // if there is a next cmd
                {
                    close(new_fd[0]); // setting up new_pipe to get output from child
                    dup2(new_fd[1], 1);
                    close(new_fd[1]);

                    err = execvp(commands_to_exec[i]->args[0], commands_to_exec[i]->args);
                    status = err;
                    exit(err);
                }
            }
            else
            {
                waitpid(pid, &status, 0);
                if (status == -1)
                    exit(1);

                if (i != 0) // if there a previous command
                {
                    close(old_fd[0]);
                    close(old_fd[1]);
                }
                if (i+1 < num_commands_p) // if there a next cmd
                {
                    old_fd[0] = new_fd[0];
                    old_fd[1] = new_fd[1];
                }
                exit(0);
            } // end if
        } // end for

        if (i) // if there a multiple commands
        {
            close(old_fd[0]);
            close(old_fd[1]);
        }
    }
    else // in the parent process we are waiting for child to handle multiple pipes
        waitpid(cpid, &status, 0);
}

函数execvp()接受结构数组.我检查了所有解析部分,它工作正常.这是execute_multiple_commands()函数,我遇到了麻烦.

这是struct的代码:

// name: command
// desc: holds one command (meaning that it can be
//        more than one token in that command)
//        "ls -la" will be an example of one command
//       holds num of tokens in command array
struct command
{
    char ** args;
    int num_args;
};
最佳答案
我建议一个新的策略,R2:

function do(commands)
    if commands is of size 1
        exec commands[0] || die
    split commands into c1 (first command) c2 (the rest of them)
    open
    if fork 
        close input end of pipe
        dup output of pipe to stdin
        do (c2) || die
    close output end of pipe
    dup input of pipe to stdout
    exec c1 || die

使用递归函数,尤其是在维护列表时,将帮助您简化逻辑.你真的不必担心堆栈深度,因为你的整个地址空间都会被覆盖.

在其他新闻中,来自man page

After a successful return from one of these system calls, the old and
new file descriptors may be used interchangeably. They refer to the
same open file description (see open(2)) and thus share file offset
and file status flags; for example, if the file offset is modified by
using lseek(2) on one of the descriptors, the offset is also changed
for the other.

当你说你关闭管道的两端时,这意味着什么?你真的在关闭它 – 它和你的程序打算使用的标准输入/输出.

– >多次编辑< - 正如Jonathan Leffler指出的那样,上述信息是正确的.我用以下程序确认了它:

#include <unistd.h>

int main(){
    dup2(0, 7);
    write(7, "Hey, 1\n", 7);
    close(0);
    write(7, "Hey, 2\n", 7);
    close(7);
    write(7, "Hey, 3\n", 7);
}

这导致以下输出:

$gcc dup2Test.c && ./a.out
Hey, 1
Hey, 2

谢谢你,乔纳森!

转载注明原文:使用系统调用fork()的多个管道实现execvp()wait()pipe() – 它根本不起作用 - 代码日志