【问题标题】:Trying to use pipe to read from / write to another program尝试使用管道读取/写入另一个程序
【发布时间】:2017-10-20 14:04:26
【问题描述】:

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

 #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 是一个从标准输入读取输入并打印输入的字符串的简单程序。 在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;
}

如果运行 program2 成功读取 program1 的输出。 它获取用户输入,但未能将用户输入的字符串提供给program1。

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



^C

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

【问题讨论】:

  • scanf("%s", &amp;str); 是错误的。 %s 需要一个指向 char 的指针,但您正在传递一个指向 char 数组 30 的指针。只需删除运算符的地址,数组就会在该上下文中转换为指向 char 的指针。与您的问题无关,但无论如何您都应该解决一些问题。
  • input = malloc(sizeof(char) * (len)); strncpy(input, buf, (len)); 你需要多分配一个字符。在这种特殊情况下,strncpy() 将 not NUL 终止字符串。顺便说一句:sizeof(char) 根据定义为 1,而 strncpy() 是一个几乎无用的函数。
  • 我改变了 malloc(sizeof(char) * (len));到 malloc(sizeof(char) * (len+1));并添加了一行 input[len] = '\0';在 strncpy() 函数之后。但结果是一样的。
  • 您考虑过使用popen(3) 吗?

标签: c string pipe


【解决方案1】:

您的主要问题是写入孩子的数据没有以换行符结尾,因此孩子不知道消息已完成(它不完整)并且孩子仍在忙于阅读而父母等待响应——死锁。

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

原始程序需要两个大量输入(一个响应来自read_write 的提示,另一个响应回显的输出),但是当它尝试将第二个输入发送到现在 -退出的孩子。下面的代码通过忽略 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
$

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多