在 C 中重现 bash 管道的行为

Reproducing the behavior of bash's pipes in C

我正在尝试使用 pipedup2 重现 bash 管道的行为,它对一个管道工作正常,因为第一个和最后一个管道要么输入或输出重定向,但是当我连续添加多个时它不起作用(当输入和输出都重定向到管道时),输入和输出似乎相互混淆,所以我们需要在两者之间睡觉吗每个命令?我在阅读文档时看到可执行文件同时 运行 并且我们不应该等待前一个子进程结束所以我不等待。这是我的代码的错误部分:

#include "shell.h"

static int  pfd[2];

void    ms_terminate(int ret) {
    close(pfd[0]);
    close(pfd[1]);
    clean_exit(ret);
}

pid_t   execute(const t_shcmd *cmd, char *const *envp, bool in, bool out) {
    pid_t   cpid = 0;

    if (!ft_strcmp(cmd->av[0], "cd")) {
        int fd_in = dup(0), fd_out = dup(1);
        if (in) dup2(pfd[0], 0);
        if (out) dup2(pfd[1], 1);
        close(pfd[0]);
        close(pfd[1]);
        builtin_cd(cmd->ac, cmd->av);
        dup2(fd_in, 0);
        dup2(fd_out, 1);
        close(fd_in);
        close(fd_out);
    }
    else if (!(cpid = fork())) {
        if (in) dup2(pfd[0], 0);
        if (out) dup2(pfd[1], 1);
        close(pfd[0]);
        close(pfd[1]);
        execve(*cmd->av, cmd->av, envp);
        clean_exit(EXIT_FAILURE);
    }
    if (cpid == -1)
        ms_terminate(EXIT_FAILURE);
    return cpid;
}

void    execute_pipeline(const t_shcmd *cmd, char *const *envp) {
    pid_t   cpid;

    if (!(cpid = fork())) {
        execute(cmd, envp, false, true);
        while ((cmd = cmd->pipe)->pipe)
            execute(cmd, envp, true, true);
        execute(cmd, envp, true, false);
        ms_terminate(EXIT_SUCCESS);
    }
    if (cpid == -1) ms_terminate(EXIT_FAILURE);
    while (wait(NULL) > 0);
}

void    ms_execute(const t_shcmd *cmd, char *const *envp) {
    pid_t           cpid;

    pipe(pfd);
    while (cmd) {
        if (cmd->pipe)
            execute_pipeline(cmd, envp);
        else {
            cpid = execute(cmd, envp, false, false);
            wait(NULL);
        }
        cmd = cmd->next;
    }
    close(pfd[0]);
    close(pfd[1]);
}

struct t_shcmd 基本上是一个像这样声明的特殊链表,并由我的解析器获取(顺便说一句,问题不是来自解析器,而是来自上面的代码):

typedef struct s_shcmd  t_shcmd;

struct s_shcmd {
    t_shcmd     *next, *pipe;
    int         ac;
    char        *av[];
};

结构如下:

把管道想象成真正的管道。

如果要将来自 prog1 的信息传送到 prog2,则需要一个管道。

如果您还想将来自 prog2 的信息传送到 prog3,您将需要另一个管道。

所以prog1 | prog2 | prog3需要两个管道。


注意:确保关闭所有不需要的管端。 prog1 应该只打开四个管端之一,prog2 应该只打开两个,prog3 应该只打开最后一个。