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