我有C代码正在模拟bash命令ls | wc
。我要实现的目标之一是能够读取每个命令的输出,以便可以打印它们-当ls
被管道传输时,它们既可以打印wc
也可以打印ls
。我面临的这个问题是,每当我阅读其中一个命令时,我都会以某种方式失去另一个命令。
观察以下代码
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#define LS_PATH "/bin/ls"
#define WC_PATH "/usr/bin/wc"
int main()
{
pid_t pid;
int link[2], link2[2];
char *const arg1[] = {"ls", NULL};
char *const arg2[] = {"wc", NULL};
char *buffer1[4096], buffer2[4096];
pipe(link);
pipe(link2);
pid = fork();
if (pid == 0)
{
dup2(link[1], STDOUT_FILENO);
close(link[0]);
close(link[1]);
execv(LS_PATH, arg1);
perror("error1");
}
else
{
pid = fork();
if (pid == 0)
{
dup2(link[0], STDIN_FILENO);
dup2(link2[1], STDOUT_FILENO);
close(link[1]);
close(link[0]);
close(link2[1]);
close(link2[0]);
execv(WC_PATH, arg2);
perror("error2");
}
else
{
close(link[1]);
close(link2[1]);
// the following two lines of code is the point of interest
read(link[0], buffer1, sizeof(buffer1)); // ls
read(link2[0], buffer2, sizeof(buffer2)); // wc
printf("%s\n", buffer1);
printf("%s\n", buffer2);
}
}
}
主要关注以下代码语句:
read(link[0], buffer1, sizeof(buffer1)); // ls
read(link2[0], buffer2, sizeof(buffer2)); // wc
ls
首先被读取到buffer1
中,它可以正常打印,但是随后读取buffer2
的{{1}}仅返回0。
如果我要切换上述代码语句的顺序,以便获得以下内容:
wc
然后将read(link2[0], buffer2, sizeof(buffer2)); // wc
read(link[0], buffer1, sizeof(buffer1)); // ls
读入wc
就像在终端中运行命令buffer2
一样工作正常,但是ls | wc
中的ls
无法打印
我不能全都得到,只有一个。
如何同时获得两者?
答案 0 :(得分:0)
if (pid == 0)
{
dup2(link[0], STDIN_FILENO); // ** HERE **
dup2(link2[1], STDOUT_FILENO);
close(link[1]);
close(link[0]);
close(link2[1]);
close(link2[0]);
execv(WC_PATH, arg2);
perror("error2");
}
请注意,wc
从link[0]
中读取。
// the following two lines of code is the point of interest
read(link[0], buffer1, sizeof(buffer1)); // ls ** HERE **
read(link2[0], buffer2, sizeof(buffer2)); // wc
请注意,父进程也尝试读取link[0]
。
您不能同时获得两者,因为写入管道的数据只能从其中读取一次。如果wc
读取ls
的输出以计算其字符,那么您也无法在父级中读取ls
的输出。如果父级从管道中读取ls
的输出,那么wc
也将无法获得它,因为它不再在管道中。这就是为什么您只能获得其中一个的原因-如果一件事读到管道,那么另一件事就不在管道中。
有多种方法可以解决此问题,具体取决于您的要求。
您可以使用三个管道,其中多余的管道是wc
的标准输入。父进程必须将数据从具有ls
输出的管道复制到作为wc
输入的管道。
您可以添加第三个进程,即复制数据的tee
实例。
您可以ls
写入临时文件。 wc
和您的父进程都可以读取该文件。与管道不同,文件允许多次读取相同的数据。