与2个Python子进程进行循环管道

时间:2016-08-30 15:57:09

标签: python-3.x subprocess infinite-loop communication popen

我需要有关子进程模块的帮助。这个问题可能听起来很重复,我在很多方面看过很多与之相关的文章。但即便如此,我也无法解决我的问题。具体如下:
我有一个C程序2.c它的内容如下:

#include<stdio.h>
int main()
{
int a;
scanf("%d",&a);
while(1)
  {
   if(a==0)               //Specific case for the first input
     {
      printf("%d\n",(a+1));
      break;
     }
   scanf("%d",&a);
   printf("%d\n",a);
  }
return 0;
}

我需要编写一个python脚本,首先使用subprocess.call()编译代码,然后使用Popen打开两个进程来执行相应的C程序。现在,第一个过程的输出必须是第二个过程的输入,反之亦然。基本上,如果我的初始输入为0,则第一个过程输出2,由第二个过程输出。它反过来输出3等等。

以下脚本是我的想法,但它有缺陷。如果有人可以帮助我,我会非常感激。

from subprocess import *
call(["gcc","2.c"])
a = Popen(["./a.out"],stdin=PIPE,stdout=PIPE) #Initiating Process
a.stdin.write('0')
temp = a.communicate()[0]
print temp
b = Popen(["./a.out"],stdin=PIPE,stdout=PIPE) #The 2 processes in question
c = Popen(["./a.out"],stdin=PIPE,stdout=PIPE)
while True:
    b.stdin.write(str(temp))
    temp = b.communicate()[0]
    print temp
    c.stdin.write(str(temp))
    temp = c.communicate()[0]
    print temp
a.wait()
b.wait()
c.wait()

2 个答案:

答案 0 :(得分:1)

问题在这里

while True:
    b.stdin.write(str(temp))
    temp = b.communicate()[0]
    print temp
    c.stdin.write(str(temp))
    temp = c.communicate()[0]
    print temp

一旦communicate返回,它会注意到更多。您必须再次运行该过程。另外,您不需要同时打开2个进程。

此外,初始阶段与运行阶段没有区别,除了您提供输入数据。

您可以采取哪些措施来简化并使其发挥作用:

from subprocess import *
call(["gcc","2.c"])
temp = str(0)

while True:
    b = Popen(["./a.out"],stdin=PIPE,stdout=PIPE) #The 2 processes in question
    b.stdin.write(temp)
    temp = b.communicate()[0]
    print temp
    b.wait()

另外,要看到并行运行的2个进程,证明你可以这样做,只需修改你的循环如下(通过在循环中移动Popen调用):

while True:

    b = Popen(["./a.out"],stdin=PIPE,stdout=PIPE) #The 2 processes in question
    c = Popen(["./a.out"],stdin=PIPE,stdout=PIPE)

    b.stdin.write(str(temp))
    temp = b.communicate()[0]
    print temp
    c.stdin.write(str(temp))
    temp = c.communicate()[0]
    print temp

更好。 b输出馈送c输入:

while True:

    b = Popen(["./a.out"],stdin=PIPE,stdout=PIPE) #The 2 processes in question
    c = Popen(["./a.out"],stdin=b.stdout,stdout=PIPE)

    b.stdin.write(str(temp))
    temp = c.communicate()[0]
    print temp

答案 1 :(得分:1)

如果您希望第一个命令a的输出作为第二个命令b的输入而转而b的输出为{{1}输入一个像蛇一样吃掉它的尾巴 - 然后你就不能在循环中使用a.communicate()不会返回,直到过程是死了,所有的输出都被消耗了。

一种解决方案是使用命名管道(如果.communicate()在这种情况下在您的系统上没有阻止):

open()

它模仿来自@alexander barakin answer#!/usr/bin/env python3 import os from subprocess import Popen, PIPE path = 'fifo' os.mkfifo(path) # create named pipe try: with open(path, 'r+b', 0) as pipe, \ Popen(['./a.out'], stdin=PIPE, stdout=pipe) as b, \ Popen(['./a.out'], stdout=b.stdin, stdin=pipe) as a: pipe.write(b'10\n') # kick-start it finally: os.remove(path) # clean up shell命令。

Serpiente alquimica

这是更复杂的解决方案,它通过python父进程汇集数据:

a < fifo | b > fifo

此代码使用通过OS管道的重定向将#!/usr/bin/env python3 import shutil from subprocess import Popen, PIPE with Popen(['./a.out'], stdin=PIPE, stdout=PIPE, bufsize=0) as b, \ Popen(['./a.out'], stdout=b.stdin, stdin=PIPE, bufsize=0) as a: a.stdin.write(b'10\n') # kick-start it shutil.copyfileobj(b.stdout, a.stdin) # copy b's stdout to a' stdin 的输出连接到a的输入(如b shell命令所做的那样)。

要完成圈子,a | b的输出将使用b复制到父Python代码中的a输入。

此代码可能有buffering issues:进程之间有多个缓冲区:C stdio缓冲区,包装管道的Python文件对象中的缓冲区(由shutil.copyfileobj()控制)。

pipe/stdio buffers

bufsize关闭Python端的缓冲,数据一旦可用就会被复制。请注意,bufsize=0可能导致部分写入 - 您可能需要内联bufsize=0并再次调用write()直到写入所有读取数据。

调用copyfileobj(),使stdout行缓冲在C程序中:

setvbuf(stdout, (char *) NULL, _IOLBF, 0)

输出

#include <stdio.h>

int main(void)
{
  int a;
  setvbuf(stdout, (char *) NULL, _IOLBF, 0); /* make line buffered stdout */
  do {
    scanf("%d",&a);
    printf("%d\n",a-1);
    fprintf(stderr, "%d\n",a); /* for debugging */
  } while(a > 0);
  return 0;
}

输出相同。

由于编写和执行C子程序的方式,您可能还需要在10 9 8 7 6 5 4 3 2 1 0 -1 和/或BrokenPipeError({{}的末尾捕获并忽略a.stdin.write()异常。当a.stdin.close())中存在未复制的数据时,1}}进程可能已经死亡。