【问题标题】:Pipe chaining in my own shell implementation我自己的 shell 实现中的管道链接
【发布时间】:2017-03-26 15:54:06
【问题描述】:

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

  • 猫 | ls(或任何不从标准输入读取的命令作为管道的最终元素)。

在这种情况下,shell 中发生的情况是 ls 执行并且 cat 在退出之前要求一行(我猜是 SIGPIPE 导致的)。我试图按照本教程更好地理解多管道背后的原理:http://web.cse.ohio-state.edu/~mamrak.1/CIS762/pipes_lab_notes.html

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

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 -c 10 将永远运行...

如果有人对此问题有解决方案或至少提供一些指导,将不胜感激。

【问题讨论】:

  • 我对命令的用处不感兴趣。我只对复制 bash 行为感兴趣,即:首先显示 ls 的输出,然后运行 ​​cat 将只占用一行(即提示用户输入一行)
  • 在您的示例中,cat 成为孙子,而 ls 是孩子。 wait 只等待ls。您应该改为将 catls 都设为直接子级,然后等待两者。
  • @thatotherguy 是的,确实我刚刚试验了你的解决方案,它确实有效。这个解决方案似乎与我链接的教程中提供的第一个想法很接近。但是,如果我想为多个命令扩展它会发生什么?这就是为什么第二个实现对我来说更有趣的原因!我应该能够有许多链式命令。有了这个解决方案,似乎我必须提前知道我必须准备多少个命令来准备管道和叉子?
  • 第二个想法(以及您的原始帖子)必然只等待管道中的最后一个命令。这在POSIX shells 中是明确有效的行为,但这不是 Zsh 和 Bash 所做的。如果您需要 Zsh 行为,则必须以第一种方式进行。您可以同时使用未知数量的元素(类似于链表的迭代和递归遍历)。
  • @thatotherguy 好的,我想我明白了,我会在明天尝试实现它,并最终提出解决方案。感谢您的帮助!

标签: c bash shell


【解决方案1】:

在考虑了@thatotherguy 的 cmets 之后,我找到了在我的代码中实现的解决方案。请记住,应该检查 pipefork 调用是否有错误,但此版本旨在尽可能简单。我的一些内置命令也需要额外的 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));
}

我对我发布的原始链接以及处理链式管道的不同方式感到困惑。从我原来的问题 (http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_02) 下面发布的 POSIX 的链接看来:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-01
    相关资源
    最近更新 更多