C 我自己的shell实现中的管道链接

C 我自己的shell实现中的管道链接,c,bash,shell,C,Bash,Shell,我目前正在用C编写自己的shell实现。我理解管道和重定向fds背后的原理。但是,管道的某些特定行为引起了我的注意: cat | ls(或任何不作为管道最终元件从stdin读取的命令) 在这种情况下,shell中发生的是ls执行,cat在退出之前请求一行代码(我猜是由SIGPIPE产生的)。为了更好地理解多管道背后的原理,我尝试遵循本教程: 下面是我编写的一些代码,试图复制我正在寻找的行为: char *cmd1[] = {"/bin/cat", NULL}; char *cmd

我目前正在用C编写自己的shell实现。我理解管道和重定向fds背后的原理。但是,管道的某些特定行为引起了我的注意:

  • cat | ls(或任何不作为管道最终元件从stdin读取的命令)
在这种情况下,shell中发生的是ls执行,cat在退出之前请求一行代码(我猜是由SIGPIPE产生的)。为了更好地理解多管道背后的原理,我尝试遵循本教程:

下面是我编写的一些代码,试图复制我正在寻找的行为:

char    *cmd1[] = {"/bin/cat", NULL};
char    *cmd2[] = {"/bin/ls", NULL};
int     pdes[2];
pid_t   child;

if (!(child = fork()))
{
    pipe(pdes);
    if (!fork())
    {
        close(pdes[0]);
        dup2(pdes[1], STDOUT_FILENO);
        /* cat command gets executed here */
        execvp(cmd1[0], cmd1);
    }
    else
    {
        close(pdes[1]);
        dup2(pdes[0], STDIN_FILENO);
        /* ls command gets executed here */
        execvp(cmd2[0], cmd2);
    }
}
wait(NULL);
我知道该实现的安全缺陷,但这只是为了测试。据我所知,该代码的问题在于,每当执行ls时,它都会退出,然后cat会以某种方式在后台运行(在我的例子中,失败是因为它在程序退出时尝试在zsh的提示下读取)。我找不到一个解决办法让它像它应该的那样工作。因为如果我一个接一个地等待命令,像cat/dev/random | head-c10这样的命令将永远运行


如果任何人对此问题有一个解决方案或至少有一些指导,我们将不胜感激。

在考虑了@thatotherguy的评论后,这里是我在代码中发现的解决方案。请记住,应该检查管道分叉调用是否存在错误,但此版本应尽可能简单。我的一些内置命令还需要额外的exit调用

void    exec_pipe(t_ast *tree, t_sh *sh)
{
    int     pdes[2];
    int     status;
    pid_t   child_right;
    pid_t   child_left;

    pipe(pdes);
    if (!(child_left = fork()))
    {
        close(pdes[READ_END]);
        dup2(pdes[WRITE_END], STDOUT_FILENO);
        /* Execute command to the left of the tree */
        exit(execute_cmd(tree->left, sh));
    }
    if (!(child_right = fork()))
    {
        close(pdes[WRITE_END]);
        dup2(pdes[READ_END], STDIN_FILENO);
        /* Recursive call or execution of last command */
        if (tree->right->type == PIPE_NODE)
            exec_pipe(tree->right, sh);
        else
            exit(execute_cmd(tree->right, sh));
    }
    /* Should not forget to close both ends of the pipe */
    close(pdes[WRITE_END]);
    close(pdes[READ_END]);
    wait(NULL);
    waitpid(child_right, &status, 0);
    exit(get_status(status));
}
我对我发布的原始链接和处理链接管道的不同方式感到困惑。从我的原始问题()下方发布的POSIX文档链接来看:

如果管道不在后台(参见异步列表),shell将等待管道中指定的最后一个命令完成,也可以等待所有命令完成


因此,这两种行为都被接受:等待最后一个命令,或者等待所有命令。我选择实现第二个行为来坚持bash/zsh的做法。

我对命令的有用性不感兴趣。我只对复制bash行为感兴趣,即:首先显示ls的输出,然后运行只占用一行的cat(即提示用户输入一行)。在您的示例中,
cat
成为孙子,而
ls
是子对象<代码>等待仅等待
ls
。相反,你应该让
cat
ls
都指导孩子们,然后等待这两个孩子。@TheotherGuy是的,我刚刚试验了你的解决方案,它确实有效。这个解决方案似乎接近我链接的教程中提供的第一个想法。但是,如果我想将其扩展到多个命令,会发生什么?这就是为什么第二个实现对我来说更有趣的原因!我应该可以有很多链接命令。有了这个解决方案,我似乎必须提前知道我需要准备多少个命令来准备管道和分叉不?第二个想法(以及您最初的帖子)必然只等待管道中的最后一个命令。这在中是显式有效的行为,但不是Zsh和Bash所做的。如果你需要Zsh行为,你必须用第一种方法。你可以用未知数量的元素(这类似于链表的迭代遍历和递归遍历)。@其他人好的,我想我明白了,我明天会尝试实现它,并最终给出解决方案。谢谢你的帮助!