管道输入和读取另一个可执行文件的输出

时间:2016-03-08 23:23:46

标签: python interprocess

我想写一个有点像hadoop流的脚本:我提供一个随机的"客户端"程序的路径,从我的主机python脚本I" pipe"字符串到客户端,我想在我的python程序中接收客户端的标准输出。

例如,如果我有以下python基本python客户端" client.py":

import sys

for line in sys.stdin:
    print("printing : " + line)

我希望,从我的python主机,能够调用可执行文件" python client.py",提供列表["a", "b"],然后接收["printing a", "printing b"] as结果。

这是我在主机代码中尝试的内容:

import subprocess    
proc = subprocess.Popen("python client.py",stdout=subprocess.PIPE, stdin=subprocess.PIPE)

for text in ["a", "b"]
    print(text)
    proc.stdin.write(bytes(text, 'UTF-8'))
    result = proc.stdout.read()
    print ("result " + str(result))
    self.proc.wait()

然而(在Windows上)它执行print(text),然后打开一个仍然冻结的python.exe窗口.... 有谁知道如何完成我想要做的事情?理想情况下应该适用于Windows和Linux

编辑:在我的实际应用程序中,传输到stdin的数据量是每行约1万个字符的10000行,所以我不能一次发送所有数据 来自stdout的内容应该是每个10个字符的10000行左右

2 个答案:

答案 0 :(得分:2)

问题是read()尝试读取整个流,这意味着它等待子进程终止。您需要确定一种方法来了解角色何时可用。以下是一些方法:

  1. 一次读取一个字符,直到返回字符(行尾)。
  2. 子应用程序可以发送恒定长度的输出。您可以在read方法中指定字符长度。
  3. 子应用程序可以宣告它将打印多少个字符。
  4. 您还需要一个条件来告诉子流程结束。例如,当它收到一个特殊字符串时。

    另一个问题可能来自缓冲:在写入操作之后可能不会立即传输数据。在这种情况下,您可以使用flush()来保证投放。

    我知道上面的代码是在python3中,但为了避免unicode转换的问题,以下程序在python2中。将它们转换为python3应该没有问题。

    计划client.py

    # pyhton2                             
    import sys
    do_run = True
    while do_run:
      i = ''
      line = ''
      while i != '\n':   # read one char at a time until RETURN
        i = sys.stdin.read(1)
        line += i
      #                                   
      if line.startswith("END"):
        do_run = False
      else:
        sys.stdout.write("printing : " + line)  # RET already in line
        sys.stdout.flush()
    

    计划main.py

    from subprocess import Popen, PIPE
    
    proc = Popen(["python2","client.py"], stdout=PIPE, stdin=PIPE, stderr=PIPE )
    
    for text in ('A', 'B', 'C', 'D', 'E'):
      print text
      proc.stdin.write(text+"\n")
      proc.stdin.flush()
      i = ''
      result_list=[]
      while i != '\n':
        i = proc.stdout.read(1)
        result_list.append(i)
      print ("result " + "".join(result_list))
    
    proc.stdin.write("END\n")
    

    我在Raspberry Pi(Rasbian)上运行了以下程序并且它有效。但是,如果我用flush()注释了这些行,程序就会卡住。

    这些程序使用第一个选项(一次读取一个char),这可能是最慢的。您可以使用其他两个来提高速度,但代价是更复杂的代码。

答案 1 :(得分:1)

为了与子流程进行交互(例如,阅读'提示'并对它们作出反应)pexpect是要走的路:

https://pexpect.readthedocs.org/en/stable/

但是,如果你不关心互动"智能"并且只是想发送一堆线并回应它们......

在client.py中

from sys import stdin

for line in stdin:
    print(line,end="")

并在您的主机文件中:

from subprocess import Popen, PIPE

text = b"a\nb\n"

sub = Popen(["python3","client.py"],stdout=PIPE,stdin=PIPE).communicate(text)

print(sub[0].decode())

根据您的编辑,请参阅下面的新主机文件:

import os
from pty import fork
from time import sleep

inputs = [b"a",b"b"]

parent, fd = fork()

if not parent:
    os.execv("/usr/bin/python3",["usr/bin/python3","/path/to/file/client.py"])

for each in inputs:
    os.write(fd,each+b'\n')
    sleep(0.5)
    os.read(fd,len(each)) #We have to get rid of the echo of our write
    print(os.read(fd,200).decode().strip())

在客户端使用与Popen一起使用的sys.stdin方法也存在问题,因为当客户端启动时输入不存在,所以我们需要阻止它。一个(非常简单的)例子:

i = input()
print("printing {0}".format(i))
i = input()
print("printint {0}".format(i))

这在Windows上不起作用(除非有人在那里实现分叉并且我不知道)。我不确定如何在Windows中执行此操作,因为我没有时间在那里。

这里有很大的局限性。它的同步,一个和os.read()并不是很高的水平。