LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 933|回复: 7

问个pipe方式重定向的问题

[复制链接]
发表于 2005-6-12 19:53:17 | 显示全部楼层 |阅读模式
这是在linux下的一个程序,附一是主程序,附二是个简单的打印"hello,world"
的程序
我想实现的功能是,在主程序中调用helloworld程序,并将helloworld的输出的结
果通过
pipe方式在主程序中打印出来(或者保存到主程序中的一个变量)。
但是下面的程序为什么有时能够正确运行,而有时又对helloworld的输出没有重定
向呢?
请大虾帮忙看看。谢谢先

附一: test.cpp


  1. #include <iostream>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <ctype.h>
  7. #include <signal.h>
  8. #include <errno.h>
  9. #include <fcntl.h>

  10. #define BUF_SIZE 1024

  11. using namespace std;

  12. /* engine related elements */
  13. int to_eng[2];  /* pipe used to send commands to chinese chess engine *
  14. /
  15. int from_eng[2]; /* pipe used to recieve engine response */
  16. void start_engine(char * cmd);  /* start chess engine and set the pipes
  17. */
  18. void send_to_eng(char *msg);
  19. int recv_from_eng(char *msg);

  20. void start_engine(char *cmd) {
  21.         char *argv[64], *p;
  22.         char buf[BUF_SIZE];
  23.         int pid,k;
  24.         printf("command used to start engine : %s\n", cmd );
  25.         /* create the pipes */
  26.         signal(SIGPIPE, SIG_IGN);
  27.     if (pipe(to_eng)) {
  28.         perror("creating pipe for send\n");
  29.         exit(EXIT_FAILURE);
  30.     }
  31.     if (pipe(from_eng)) {
  32.         perror("creating pipe for recieve\n");
  33.         exit(EXIT_FAILURE);
  34.     }

  35.     /* split the commands in file and argv */
  36.         k=0;
  37.         strcpy(buf, cmd);
  38.         p = buf;
  39.         while (1) {
  40.                 argv[k++] = p;
  41.                 p = strchr(p, ' ');
  42.                 if ( p == NULL ) break;
  43.                 *p++ = '\0';
  44.         }
  45.         argv[k] = NULL;

  46.         /* start the engine */
  47.         if ( (pid = fork()) ==  0 ) {
  48.                 /* the child */
  49.                 dup2(to_eng[0], 0);
  50.                 dup2(from_eng[1], 1);

  51.                 close(to_eng[0]);
  52.                 close(to_eng[1]);
  53.                 close(from_eng[0]);
  54.                 close(from_eng[1]);
  55.                 dup2(1, fileno(stderr)); /* redirect stderr to the pipe */
  56.         //dup2(1, fileno(stdout)); /* redirect stderr to the pipe */

  57.         perror("faint!");
  58.         execvp(argv[0], argv);

  59.                 perror(argv[0]);
  60.         fflush(stdout);
  61.         fflush(stderr);

  62.                 exit( EXIT_FAILURE );

  63.         } else if (pid < 0) {
  64.         perror("fork");
  65.         exit(EXIT_FAILURE);
  66.         } else {
  67.                 /* the parent */
  68.                 close ( to_eng[0]);
  69.                 close ( from_eng[1]);

  70.         /* set nonblocking read */
  71.                 fcntl( from_eng[0], F_SETFL, O_NONBLOCK);
  72.         }
  73. }

  74. void send_to_eng(char *msg) {
  75.         write ( to_eng[1], msg, strlen(msg));
  76. }

  77. int recv_from_eng(char *msg){
  78.         memset( msg, '\0', BUF_SIZE );
  79.         int nread = read( from_eng[0], msg, BUF_SIZE );
  80.     cout << "nread=" << nread << endl;

  81.         return strlen(msg);
  82. }

  83. int main()
  84. {
  85.     char msg[BUF_SIZE];

  86.     start_engine("helloworld");

  87.         /* empty the pipe*/
  88.     while(recv_from_eng(msg))
  89.     {
  90.         cout << msg;
  91.     }
  92. }
复制代码

附二: helloworld.cpp


  1. #include <cstdio>

  2. using namespace std;

  3. int main()
  4. {
  5.     printf("Hello,world!\n");
  6.     fflush(stdout);
  7. }

复制代码
发表于 2005-6-12 20:23:47 | 显示全部楼层
在你运行的“helloworld”里面要 fflush 一下,如果“helloworld”不能改的话,这样的重定向需要用伪终端来实现了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-12 23:51:53 | 显示全部楼层
伪终端是什么概念?
你说的fflush不是我代码中的那个fflush吗?
回复 支持 反对

使用道具 举报

发表于 2005-6-13 12:07:42 | 显示全部楼层
哦,不好意思,没看清你的“附二”,原来你已经 fflush 了。

是这一句惹的祸:
        /* set nonblocking read */
                fcntl( from_eng[0], F_SETFL, O_NONBLOCK);

你设置了非阻塞读,实际上有时还没读到你就输出了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-14 14:53:53 | 显示全部楼层
谢谢你的回复,问题确实是在这里。不过这个还不是我想实现的功能。在我的想实现的功能中可能还不希望主程序被阻塞。

我想实现的功能是,在程序test中输入命令,将命令重定向到程序engine, 然后
将engine的返回结果在当前进程中打印出来。

下面是我新编的代码,跟前面略有不同,
它包括两个程序代码,附一是test.cpp,附二是engine.cpp

在我这里如果编译后直接运行./test,则无法返回engine程序的字串
但是我在用gdb调试的时候就可以按照理想的顺序返回。

哪位大虾能帮忙看看问题出在哪里?非常感谢。

附一:test.cpp

  1. #include <iostream>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <signal.h>

  6. #define BUF_SIZE 1024

  7. using namespace std;

  8. /* engine related elements */
  9. int to_eng[2];         /* pipe used to send commands to the  engine */
  10. int from_eng[2]; /* pipe used to recieve engine response */
  11. void start_engine();         /* start  engine and set the pipes */
  12. void send_to_eng(char *msg);
  13. int recv_from_eng(char *msg);

  14. void start_engine() {
  15.         char *argv[64], *p;
  16.         char buf[BUF_SIZE];
  17.         int pid,k;
  18.         /* create the pipes */
  19.         if(signal(SIGPIPE, SIG_IGN)==SIG_ERR)
  20.     {
  21.         perror("signal error");
  22.         exit(EXIT_FAILURE);
  23.     }

  24.     if (pipe(to_eng)<0 || pipe(from_eng)<0)
  25.     {
  26.         perror("creating pipe error\n");
  27.         exit(EXIT_FAILURE);
  28.     }
  29.    
  30.         /* start the engine */
  31.         if ( (pid = fork()) < 0 )
  32.     {
  33.         //perror("pid<0");
  34.         perror("fork");
  35.         exit(EXIT_FAILURE);
  36.     }
  37.     else if(pid==0)
  38.     {
  39.                 /* the child */
  40.                 dup2(to_eng[0], 0);
  41.                 dup2(from_eng[1], 1);

  42.                 close(to_eng[1]);
  43.                 close(from_eng[0]);

  44.         if(dup2(to_eng[0], STDIN_FILENO)!=STDIN_FILENO) /* redirect stdout to the pipe */
  45.             perror("dup2 error to stdin\n");
  46.         close(to_eng[0]);

  47.         if(dup2(from_eng[1], STDERR_FILENO)!=STDERR_FILENO) /* redirect stdout to the pipe */
  48.             perror("dup2 error to stdout\n");
  49.         close(from_eng[1]);

  50.         if((execl("./engine", "engine", (char *)0)) <0)
  51.         {
  52.             perror("error");
  53.         }

  54.                 perror(argv[0]);
  55.                 exit( EXIT_FAILURE );
  56.         }
  57.     else {
  58.                 /* the parent */
  59.                 close ( to_eng[0]);
  60.                 close ( from_eng[1]);

  61.         /* set nonblocking read */
  62.                 fcntl( from_eng[0], F_SETFL, O_NONBLOCK);
  63.         }
  64. }

  65. void send_to_eng(char *msg) {
  66.         write ( to_eng[1], msg, strlen(msg));
  67. }

  68. int recv_from_eng(char *msg){
  69.         memset( msg, '\0', BUF_SIZE );
  70.         int nread = read( from_eng[0], msg, BUF_SIZE );
  71.     cout << "nread=" << nread << endl;

  72.         return strlen(msg);
  73. }

  74. int main()
  75. {

  76.     char msg[BUF_SIZE];

  77.     start_engine();

  78.        
  79.     int ret = 0;
  80.     while((ret = recv_from_eng(msg))>0)
  81.     {
  82.         cout << "ret=" << ret << ":" << msg;
  83.     }

  84.     send_to_eng("change\n");

  85.     while((ret = recv_from_eng(msg))>0)
  86.     {
  87.         cout << "ret=" << ret << ":" << msg;
  88.     }

  89.     send_to_eng("stop\n");

  90.     while((ret = recv_from_eng(msg))>0)
  91.     {
  92.         cout << "ret=" << ret << ":" << msg;
  93.     }

  94.     exit(0);
  95. }
复制代码

附二:engine.cpp

  1. #include <cstdio>
  2. #include <cstdlib>
  3. #include <iostream>

  4. using namespace std;

  5. int main()
  6. {

  7.     cout << "loop begin" << endl;

  8.     int n, int1, int2;
  9.     char msg[1024];

  10.     while(1)
  11.     {
  12.         memset( msg, '\0', 1024 );
  13.         int n = read(STDIN_FILENO, msg, 1024);
  14.         
  15.         if(strstr(msg, "stop"))
  16.         {
  17.             write(STDOUT_FILENO, "stopped\n", 9);
  18.             break;
  19.         }

  20.         if(strstr(msg, "change"))
  21.             write(STDOUT_FILENO, "Changed\n", 9);
  22.         else if(strlen(msg)>0)
  23.             write(STDOUT_FILENO, "Hello,world\n", 13);
  24.     }

  25.     return 0;
  26. }
复制代码
回复 支持 反对

使用道具 举报

发表于 2005-6-15 14:01:14 | 显示全部楼层
(1)你重定向的是标准错误输出,注释却说是标准输出?

(2)在recv_from_eng中,应该返回nread,比返回strlen(msg)更可靠。

(3)如果你要用非阻塞方式,你用的循环条件就有问题了;而且既然要循环读,用非阻塞方式又有什么意义?不如用阻塞方式,我改成阻塞方式试过了,没问题。

这是我改的test.cpp

  1. #include <iostream>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <signal.h>

  6. #define BUF_SIZE 1024

  7. using namespace std;

  8. /* engine related elements */
  9. int to_eng[2];         /* pipe used to send commands to the  engine */
  10. int from_eng[2]; /* pipe used to recieve engine response */
  11. void start_engine();         /* start  engine and set the pipes */
  12. void send_to_eng(char *msg);
  13. int recv_from_eng(char *msg);

  14. void start_engine() {
  15.         char *argv[64], *p;
  16.         char buf[BUF_SIZE];
  17.         int pid,k;
  18.         /* create the pipes */
  19.         if(signal(SIGPIPE, SIG_IGN)==SIG_ERR)
  20.     {
  21.         perror("signal error");
  22.         exit(EXIT_FAILURE);
  23.     }

  24.     if (pipe(to_eng)<0 || pipe(from_eng)<0)
  25.     {
  26.         perror("creating pipe error\n");
  27.         exit(EXIT_FAILURE);
  28.     }
  29.    
  30.         /* start the engine */
  31.         if ( (pid = fork()) < 0 )
  32.     {
  33.         //perror("pid<0");
  34.         perror("fork");
  35.         exit(EXIT_FAILURE);
  36.     }
  37.     else if(pid==0)
  38.     {
  39.                 /* the child */
  40.                 dup2(to_eng[0], 0);
  41.                 dup2(from_eng[1], 1);

  42.                 close(to_eng[1]);
  43.                 close(from_eng[0]);

  44.         if(dup2(to_eng[0], STDIN_FILENO)!=STDIN_FILENO) /* redirect stdout to the pipe */
  45.             perror("dup2 error to stdin\n");
  46.         close(to_eng[0]);

  47.         [color=Red]if(dup2(from_eng[1], STDOUT_FILENO)!=STDOUT_FILENO) /* redirect stdout to the pipe */[/color]            perror("dup2 error to stdout\n");
  48.         close(from_eng[1]);

  49.         if((execl("./engine", "engine", (char *)0)) <0)
  50.         {
  51.             perror("error");
  52.         }

  53.                 perror(argv[0]);
  54.                 exit( EXIT_FAILURE );
  55.         }
  56.     else {
  57.                 /* the parent */
  58.                 close ( to_eng[0]);
  59.                 close ( from_eng[1]);

  60.         /* set nonblocking read */
  61. [color=Red]//                fcntl( from_eng[0], F_SETFL, O_NONBLOCK);[/color]
  62.         }
  63. }

  64. void send_to_eng(char *msg) {
  65.         write ( to_eng[1], msg, strlen(msg));
  66. }

  67. int recv_from_eng(char *msg){
  68.         memset( msg, '\0', BUF_SIZE );
  69.         int nread = read( from_eng[0], msg, BUF_SIZE );
  70.     cout << "nread=" << nread << endl;

  71.         return nread;
  72. }

  73. int main()
  74. {

  75.     char msg[BUF_SIZE];

  76.     start_engine();

  77.        
  78.     int ret = 0;
  79. [color=Red]
  80.     recv_from_eng(msg); // 严格的讲还是要循环的,我在这里就省了[/color]
  81.     printf("test: %s\n", msg);

  82.     send_to_eng("change\n");
  83.     ret = recv_from_eng(msg);
  84.     printf("test: %s\n", msg);

  85.     send_to_eng("stop\n");
  86.     recv_from_eng(msg);
  87.     printf("test: %s\n", msg);

  88.     exit(0);
  89. }
复制代码
回复 支持 反对

使用道具 举报

 楼主| 发表于 2005-6-22 20:12:33 | 显示全部楼层
谢谢!
问题已经解决。
原来我看到的例程是非阻塞方式的,但是人家会定时间间隔地检查子进程输出流是否有内容。
所以如果直接检查输出流的话便很可能没有任何输出。
回复 支持 反对

使用道具 举报

发表于 2005-7-25 17:58:39 | 显示全部楼层
google
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表