尝试使用管道读取/写入另一个程序

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尝试使用管道读取/写入另一个程序相关的知识,希望对你有一定的参考价值。

我正在尝试编写一个程序,它读取另一个程序的输出并写入程序作为输入。

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>

 int main(void)
 {
     char str[30];

     printf("Input string : ");
     fflush(stdout);
     scanf("%s", &str);
     fflush(stdout);

     printf("entered string is %s\n", str);
     return 0;
 }

此程序1是从stdin读取输入的简单程序,并打印输入的字符串。在program2中,我尝试创建2个管道并执行program1。并读取program1的输出并获取用户输入并将输入的字符串用户传递给program1。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

typedef struct pipe_rw
{
   pid_t cpid;
   int pipe_r[2];
   int pipe_w[2];
} RWPIPE;

char *get_user_input(void)
{
   char buf[128];
   char *input;
   char ch;
   int n;
   int len = 0;

   memset(buf, 0x0, 128);
   while((ch = fgetc(stdin)) != 0xa)
   {
      buf[len] = ch;
      len++;
   }

   input = malloc(sizeof(char) * (len));
   strncpy(input, buf, (len));
   return input;
}

int pclose_rw(RWPIPE *rwp)
{
   int status, ret = 0;

   if (rwp)
   {
      if (rwp->cpid > 0)
      {
         kill(rwp->cpid, SIGTERM);

         do {
            ret = waitpid(rwp->cpid, &status, WUNTRACED|WCONTINUED);
         } while (!WIFEXITED(status) && !WIFSIGNALED(status));
      }

      close(rwp->pipe_r[0]);
      close(rwp->pipe_w[1]);
      free(rwp);
   }

   return ret;
}

RWPIPE *popen_rw(const char *command)
{
   RWPIPE *rwp = (RWPIPE *)malloc(sizeof(*rwp));
   if (rwp == NULL)
      return NULL;

   memset(rwp, 0x00, sizeof(*rwp));
   if (pipe(rwp->pipe_r) != 0 || pipe(rwp->pipe_w) != 0)
   {
      free(rwp);
      return NULL;
   }

   rwp->cpid = fork();
   if (rwp->cpid == -1)
   {
      free(rwp);
      return NULL;
   }

   if (rwp->cpid == 0)
   {
      dup2(rwp->pipe_w[0], STDIN_FILENO);
      dup2(rwp->pipe_r[1], STDOUT_FILENO);

      close(rwp->pipe_r[0]);
      close(rwp->pipe_r[1]);
      close(rwp->pipe_w[0]);
      close(rwp->pipe_w[1]);

      execl(command, command, NULL);
      printf("Error: fail to exec command - %s ..\n", command);
      exit (1);
   }
   else
   {
      close(rwp->pipe_r[1]);
      close(rwp->pipe_w[0]);
   }

   return rwp;
}

ssize_t read_p(RWPIPE *rwp, void *buf, size_t count)
{
   return read(rwp->pipe_r[0], buf, count);
}

ssize_t write_p(RWPIPE *rwp, const void *buf, size_t count)
{
   return write(rwp->pipe_w[1], buf, count);
}

int main(void)
{
   char rbuf[BUFSIZ], wbuf[BUFSIZ];
   int ret, len, n = 0;
   char *string;

   RWPIPE *rwp = popen_rw("./read_write");
   if (rwp == NULL)
   {
      printf("Error: fail to open command ..\n");
      return EXIT_FAILURE;
   }

   while (1)
   {
      memset(rbuf, 0x00, sizeof(rbuf));
      if (read_p(rwp, rbuf, sizeof(rbuf)) < 1)
      {
         printf("No more input..\n");
         break;
      }
      printf("%s", rbuf);

      string = get_user_input();
      len = strlen(string);
      ret = write_p(rwp, string, len);
      if (ret != len)
      {
         printf("Write %d bytes (expected %d) ..\n", ret, len);
         break;
      }
      printf("end");
   }
   pclose_rw(rwp);

   return EXIT_SUCCESS;
}

如果运行,程序2成功读取program1的输出。并且它获得用户输入但是它无法将从用户输入的字符串提供给program1。

[root@localhost test_code]# ./rw_pipe
Input string : 1234



^C

请给我一些想法,为什么它这样工作。

答案

您的主要问题是写入子项的数据不会以换行符结束,因此孩子不知道该消息已完成(它未完成)并且在父母等待的情况下孩子仍在忙着阅读响应 - 僵局。

此代码添加了一些检测并通过在get_input()读取的字符串中包含换行符来解决问题。

原始程序需要两个输入(一个响应来自read_write的提示,另一个响应于回显输出),但是当它试图将第二个输入发送到现在退出的子节点时死于SIGPIPE。下面的代码通过忽略SIGPIPE信号来绕过这一点,这意味着父进行写错误而不是被信号杀死。

两个程序之间有一个不寻常的控制流程,如果你将read_write变成一个迭代程序,你会发现它为单个输入生成了两个输出。当然,这不是通常的方式。但是,修复不在即时练习范围之内。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

typedef struct pipe_rw
{
   pid_t cpid;
   int pipe_r[2];
   int pipe_w[2];
} RWPIPE;

static char *get_user_input(void)
{
   char buf[128];
   char *input;
   char ch;
   size_t len = 0;

   while((ch = fgetc(stdin)) != '\n' && ch != EOF && len < sizeof(buf) - 2)
      buf[len++] = ch;
   buf[len++] = '\n';
   buf[len] = '\0';

   input = malloc(sizeof(char) * (len + 1));
   strncpy(input, buf, (len + 1));
   printf("Got: [%s]\n", input);
   return input;
}

static int pclose_rw(RWPIPE *rwp)
{
   int status, ret = 0;

   if (rwp)
   {
      if (rwp->cpid > 0)
      {
         kill(rwp->cpid, SIGTERM);

         do {
            ret = waitpid(rwp->cpid, &status, WUNTRACED|WCONTINUED);
         } while (!WIFEXITED(status) && !WIFSIGNALED(status));
      }

      close(rwp->pipe_r[0]);
      close(rwp->pipe_w[1]);
      free(rwp);
   }

   return ret;
}

static RWPIPE *popen_rw(const char *command)
{
   RWPIPE *rwp = (RWPIPE *)malloc(sizeof(*rwp));
   if (rwp == NULL)
      return NULL;

   memset(rwp, 0x00, sizeof(*rwp));
   if (pipe(rwp->pipe_r) != 0 || pipe(rwp->pipe_w) != 0)
   {
      free(rwp);
      return NULL;
   }

   rwp->cpid = fork();
   if (rwp->cpid == -1)
   {
      free(rwp);
      return NULL;
   }

   if (rwp->cpid == 0)
   {
      dup2(rwp->pipe_w[0], STDIN_FILENO);
      dup2(rwp->pipe_r[1], STDOUT_FILENO);

      close(rwp->pipe_r[0]);
      close(rwp->pipe_r[1]);
      close(rwp->pipe_w[0]);
      close(rwp->pipe_w[1]);

      execl(command, command, NULL);
      fprintf(stderr, "Error: fail to exec command '%s'.\n", command);
      exit (1);
   }
   else
   {
      close(rwp->pipe_r[1]);
      close(rwp->pipe_w[0]);
   }

   return rwp;
}

static ssize_t read_p(RWPIPE *rwp, void *buf, size_t count)
{
   return read(rwp->pipe_r[0], buf, count);
}

static ssize_t write_p(RWPIPE *rwp, const void *buf, size_t count)
{
   return write(rwp->pipe_w[1], buf, count);
}

int main(void)
{
   char rbuf[BUFSIZ];
   int ret, len;
   char *string;

   signal(SIGPIPE, SIG_IGN);

   RWPIPE *rwp = popen_rw("./read_write");
   if (rwp == NULL)
   {
      printf("Error: fail to open command ..\n");
      return EXIT_FAILURE;
   }

   while (1)
   {
      memset(rbuf, 0x00, sizeof(rbuf));
      if (read_p(rwp, rbuf, sizeof(rbuf)) <= 0)
      {
         printf("No more input..\n");
         break;
      }
      printf("From child: [%s]\n", rbuf);

      string = get_user_input();
      len = strlen(string);
      printf("Length %d: [%s]\n", len, string);
      ret = write_p(rwp, string, len);
      if (ret != len)
      {
         fprintf(stderr, "Write %d bytes (expected %d) ..\n", ret, len);
         break;
      }
      printf("end cycle\n");
   }
   printf("End of loop\n");
   pclose_rw(rwp);

   return EXIT_SUCCESS;
}

样品运行

该计划是rwpipe53;我输入的输入是OcelotGrumble

$ ./rwpipe53
From child: [Input string : ]
Ocelot
Got: [Ocelot
]
Length 7: [Ocelot
]
end cycle
From child: [entered string is Ocelot
]
Grumble
Got: [Grumble
]
Length 8: [Grumble
]
Write -1 bytes (expected 8) ..
End of loop
$

请注意方括号(如果您愿意,可以使用任何一对标记符号)如何显示数据的开始和结束位置。调试代码时,我发现这是一种有价值的技术。

以上是关于尝试使用管道读取/写入另一个程序的主要内容,如果未能解决你的问题,请参考以下文章

fprintf vs WriteFile 写入管道:无法从所有管道读取

在C中读取和写入结构到管道

为啥在没有人阅读后继续写入命名管道?

c中的命名管道

java.io.PipedInputStream

C管道写入/读取双打序列失败