【问题标题】:Execute pipes recursively in Abstract syntax tree在抽象语法树中递归执行管道
【发布时间】:2020-01-05 17:52:10
【问题描述】:

我已经成功地为我的 minishell 创建了一个抽象语法树,事情是这样的 当我尝试执行管道命令时,我卡住了。

第一个管道执行并将结果输出到标准输出 1,而第二个 grep 文件名要么卡住要么根本不执行。

我尝试了不同的方法,得到了不同的结果,但没有一个主题有效

我将不胜感激。

这就是我的 AST 的样子。

ls -la | cat -e | grep filename

t_node      *pipe_execution(t_node *node, t_list *blt, t_line *line, int std[2])
{
    int pp[2];

    if (node)
    {
        if (node->kind == NODE_PIPE)
        {
            if (node->and_or_command->left)
            {
                pipe(pp);
                std[1] = pp[1];
                pipe_execution(node->and_or_command->left, blt, line, std);
                close(pp[1]);
            }
            if (node->and_or_command->right)
            {
                std[0] = pp[0];
                std[1] = 1;
                dprintf(2, "right std %d\n", std[1]);
                pipe_execution(node->and_or_command->right, blt, line, std);
                close(std[0]);
            }
        } else if (node->kind == NODE_SIMPLE_COMMAND)
        {
            dprintf(2, "====%s=== and stdin %d stdout %d\n", node->simple_command->head->name, std[0], std[1]);
            execute_shell(blt, line->env, node, std);
        }
    }
    return (node);
}
int        execute_shell(t_list *blt, t_list *env, t_node *node, int std[2])
{
    ...
    return (my_fork(path, env, cmds, std));
}

我的 fork 过程的实现。

int         my_fork(char *path, t_list *env, char **cmds, int std[2])
{
    pid_t       child;
    char        **env_tab;
    int         status;

    status = 0;
    env_tab = env_to_tab(env);
    child = fork();
    if (child > 0)
        waitpid(child, &status, 0);
    else if (child == 0)
    {
        dup2(std[0], 0);
        dup2(std[1], 1);
        execve(path, cmds, env_tab);
    }
    return (status);
}

我希望这段代码有意义。

【问题讨论】:

  • 怎么了?应该发生什么以及实际发生了什么?你能把它做成一个可编译的例子吗?
  • 第一个管道执行并将结果输出到标准输出 1,而第二个管道 grep filename 要么卡住要么根本不执行,因为我尝试了不同的方法。
  • 你可以请edit那个问题吗?
  • 我已经这样做了,你知道如何在走树的同时执行管道吗?

标签: c shell pipe fork abstract-syntax-tree


【解决方案1】:

管道需要并发执行

据我从您提供的代码 sn-ps 可以看出,问题是 my_fork() 正在阻塞。因此,当您执行一个进程时,您的 shell 会停止并等待该进程完成,然后再开始下一个进程。如果你做一些简单的事情,比如:

/bin/echo Hello | cat

那么管道的内部缓冲区足够大,可以存储整个输入字符串Hello。一旦/bin/echo 进程完成,您将执行cat,然后它可以从管道中读取缓冲数据。但是,一旦它变得更复杂,或者当第一个进程向管道发送更多数据时,它的内部缓冲区就会被填满,然后它就会阻塞。

解决方案是推迟在您派生的进程上调用 waitpid(),直到您生成了属于命令行一部分的所有进程。

在启动进程之前创建所有必需的管道

您的函数pipe_execution() 假设只有一个管道;它以 filedescriptor 0 作为其输入启动第一个进程,并以 filedescriptor 1 作为其输出启动第二个进程。但是,如果您在单个命令行上有多个管道,例如在 ls -la | cat -e | grep filename 中,则 cat -e 进程的输出需要进入第二个管道,而不是标准输出。

您需要在启动第一个管道的右手命令之前创建第二个管道。在启动任何命令之前创建所有管道可能是最简单的。您可以通过定义多个阶段来做到这一点:

  1. 创建管道
  2. 启动命令
  3. 等待所有命令完成

您可以多次遍历您构建的抽象语法树,每次执行一个阶段。

【讨论】:

  • 非常感谢您的洞察力,这是我走树并称管道正确的方式,因为在我看来,还有其他问题。
  • 我无法从您发布的代码中看出。例如,pipespp 是什么?它们是如何初始化的?
  • 我对代码做了一些修改,希望对你有帮助。
  • 您的代码确实会导致第一个管道右侧的输出转到stdout,因为这正是您告诉它对std[1] = 1 执行的操作。
  • 我之前尝试过这样做,但似乎要么是我对管道缺乏了解,要么会导致相同的结果。如果您能详细说明示例代码,我将不胜感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多