C编程 - 使用管道处理stdout和stdin

时间:2014-04-18 14:54:25

标签: c linux exec fork pipe

我正在写一个由父母和他的孩子(使用fork)组成的C程序。他们通过管道交流。 Parent通过标准输出写入管道,子项通过标准输入从管道读取。一旦他们连接,父母写道"你好世界"进入管道,儿子叫exec。我的代码如下所示:

int main(int argc, char *argv[])
{
 int p, a;
 char buf[1024];
 FILE *file;
 size_t nread;
 int fd[2];
 char argument[PATH_MAX];

 if(pipe(fd)<0){
 return 1;
 }

 p = fork();
 switch(p){
   case -1: perror("Error en el fork()"); return 1;
   case 0:
     close(fd[1]);
     close (0);
     dup(fd[0]);
     close(fd[0]);
     sprintf(argument,"/usr/bin/%s",argv[1]);
     execvp(argument,argv);
     perror("Error en el execv");
     exit(1);
   default: break;
 }
 close(fd[0]);
 close(1);
 a = dup(fd[1]);
 close(fd[1]);
 write(1,"Hello World\n",12);
 close(a);
 wait(NULL);
 return 0;
}
由子执行的exec函数调用函数rev或wc。如果不带参数调用,则应将rev和wc应用于标准输入(&#34; hello world&#34;在我的情况下)。但这不起作用,我不知道为什么。任何帮助都会非常感激。

2 个答案:

答案 0 :(得分:2)

这不起作用,我不知道为什么

因为您使用的是dup()。要将子进程的标准输入重定向到管道,要使用的正确系统调用是dup2()

case 0:
    close( fd[1] );
    dup2( fd[0], 0 ); // this "dup"s the read-end of the pipe onto STDIN
    close( fd[0] );

请注意,父代码分支中根本不需要dup()调用。只需写入管道的写端:

write( fd[1], "Hello World\n", 12 );

但是,如果你想在父分支中使用execvp,要启动另一个程序并重定向其标准输出,那么你也必须在这里使用dup2()

dup2( fd[1], 1 ); // this "dup"s the write-end of the pipe onto STDOUT
close( fd[1] );

阅读dup2的{​​{3}}了解详情。

此外,您的代码的另一个问题是使用带有argv的execvp作为参数列表。这将导致revwc等程序接收父程序的整个命令行,从而找到一个参数来处理而不是从标准输入读取。你可能想要

execvp( argv[1], &argv[1] );

答案 1 :(得分:1)

您在以下代码中错误地使用execvp()

 sprintf(argument,"/usr/bin/%s",argv[1]);
 execvp(argument,argv);

如果您按./a.out wc运行程序,那么在这种情况下,/usr/bin/wc的参数将为./a.out wc,即原始参数列表为./a.out,然后{{ 1}}将尝试在当前工作目录中打开名为/usr/bin/wc的文件,而不是从标准输入中读取。

所以将上面的代码更改为

wc

应该解决问题,这次 sprintf(argument,"/usr/bin/%s",argv[1]); execvp(argument,NULL); 的参数列表将是一个空列表。