简体   繁体   English

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

[英]Trying to use pipe to read from / write to another program

I'm trying to write a program which read output of another program and write to the program as input.我正在尝试编写一个程序来读取另一个程序的 output 并写入该程序作为输入。

 #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;
 }

This program1 is a simple program reading input from stdin and print the string entered.这个 program1 是一个从标准输入读取输入并打印输入的字符串的简单程序。 And here in the program2, I tried to create 2 pipes and execute the program1.在program2中,我尝试创建2个管道并执行program1。 And read the output of program1 and get user input and deliver the string user entered to program1.并读取program1的output并获取用户输入并将用户输入的字符串传递给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;
}

If run the program2 reads output of program1 successfully.如果运行program2成功读取program1的output。 And it gets user input but it failed to give the string entered from user to program1.它获取用户输入,但未能将用户输入的字符串提供给program1。

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



^C

Please give me some ideas why it works like this.请给我一些想法,为什么它会这样工作。

Your primary problem is that the data written to the child does not end with a newline, so the child is not aware that the message is complete (it isn't complete) and the child is still busy reading while the parent is waiting for a response — a deadlock. 您的主要问题是写入孩子的数据没有以换行符结尾,因此孩子不知道消息已完成(未完成),并且孩子仍在忙于阅读,而父母正在等待反应-僵局。

This code adds some instrumentation and fixes the problem by including the newline in the string read by get_input() . 此代码添加了一些工具,并通过在get_input()读取的字符串中包含换行符来解决此问题。

The original program expects two lots of input (one in response to the prompt from read_write , the other in response to the echoed output), but dies from a SIGPIPE when it tries to send the second input to the now-exited child. 原始程序需要两个输入(一个响应read_write的提示,另一个响应响应的输出),但是当它尝试将第二个输入发送给现在退出的子级时,它就从SIGPIPE中退出了。 The code below circumvents that by ignoring SIGPIPE signals, which means that the parent gets a write error instead of being killed by the signal. 下面的代码通过忽略SIGPIPE信号来规避这一问题,这意味着父级会收到写入错误,而不是被信号杀死。

There's an unusual control flow between the two programs, and if you made read_write into an iterative program, you'd see that it generates two outputs for a single input. 这两个程序之间存在异常的控制流,如果将read_write放入迭代程序中,则会看到它为单个输入生成两个输出。 That's not the way it's usually done, of course. 当然,这不是通常的方式。 Fixing that is outside of the scope of the immediate exercise, though. 但是,此修复不在即时练习的范围之内。

#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;
}

Sample run 样品运行

The program is rwpipe53 ; 该程序是rwpipe53 ; the input I typed was Ocelot and Grumble . 我输入的输入是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
$

Note how the square brackets (any pair of marker symbols can be used if you prefer) shows where the data starts and ends. 请注意方括号(如果需要,可以使用任意一对标记符号)如何显示数据的开始和结束位置。 I find that a valuable technique when debugging code. 在调试代码时,我发现这是一种有价值的技术。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM