我已经成功地为我的 minishell 制作了一个抽象语法树,事情是 当我尝试执行管道命令时,我被卡住了。
第一个管道执行并将结果输出到 stdout 1,而第二个 grep filename 要么卡住要么根本不执行。
我尝试了不同的方法,得到了不同的结果,但没有一个主题有效
如果有任何帮助,我将不胜感激。
这就是我的 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);
}
我希望这段代码有意义。
最佳答案
管道需要并发执行
据我从您提供的代码片段中可以看出,问题在于 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
进程需要进入第二个管道,而不是标准输出。
您需要在启动第一个管道的右侧命令之前创建第二个管道。在启动任何命令之前创建所有管道可能是最简单的。您可以通过定义多个阶段来做到这一点:
- 创建管道
- 启动命令
- 等待所有命令完成
您可以多次遍历您构建的抽象语法树,每次执行其中一个阶段。
关于c - 在抽象语法树中递归执行管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59602615/