Python 管道输入到另一个可执行文件并从中读取输出

Python 管道输入到另一个可执行文件并从中读取输出,python,interprocess,Python,Interprocess,我想写一个有点像hadoop流的脚本:我提供一个随机的“客户端”程序路径,从我的宿主python脚本将字符串“管道”到客户端,我想在我的python程序中接收客户端的标准输出 例如,如果我有以下python基本python客户端“client.py”: 我希望能够从python主机调用可执行文件“python client.py”,为其提供列表[“a”,“b”],然后作为结果接收[“打印a”,“打印b”] 以下是我在主机代码中尝试的内容: import subprocess proc =

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

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

我希望能够从python主机调用可执行文件“python client.py”,为其提供列表
[“a”,“b”]
,然后作为结果接收
[“打印a”,“打印b”]

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

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上)它执行
打印(文本)
,然后打开一个保持冻结状态的python.exe窗口。。。。 有人知道如何完成我想做的事情吗?最好能在windows和linux上工作

编辑:在我的实际应用程序中,要传输到stdin的数据量是10000行,每行约1K个字符,因此我不能一次发送所有数据
stdout的内容应该是大约100000行,每行10个字符

,用于与子进程交互(例如,读取“提示”并对其作出反应)。pexpect是一种方法:

然而,如果你不关心“智能化”的交互,只想发送一堆信息并回应它们

在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方法也存在问题,因为客户端启动时输入不在那里,所以我们需要将其阻塞。(非常简单)exmaple:

这在Windows上不起作用(除非有人的工具在那里分叉,我不知道)。我不知道如何在windows上做,因为我没有在那里花时间


在这方面有很大的局限性。例如,它的同步性和os.read()不是很高的级别。

对于与子进程交互(例如,读取“提示”并对其作出反应),pexpect是一种方法:

然而,如果你不关心“智能化”的交互,只想发送一堆信息并回应它们

在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方法也存在问题,因为客户端启动时输入不在那里,所以我们需要将其阻塞。(非常简单)exmaple:

这在Windows上不起作用(除非有人的工具在那里分叉,我不知道)。我不知道如何在windows上做,因为我没有在那里花时间


在这方面有很大的局限性。例如,它与os.read()的同步级别并不高。

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

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

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

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

    程序客户端.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()
    
    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")
    
    程序main.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()
    
    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()
    注释这些行,程序就会阻塞


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

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

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

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

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

    程序客户端.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()
    
    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")
    
    程序main.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()
    
    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()
    注释这些行,程序就会阻塞


    这些程序使用第一个选项(一次读取一个字符),这可能是最慢的。您可以通过使用另外两种方法来提高速度,而代价是编写更复杂的代码。

    谢谢!但是cf my edit:我打算向输入流发送成吨的数据,因此它必须是一个真实的流,对于stdoutYeah也是如此,发布速度太慢。更新。hthI在我的第一次更新中有一些错误——我没有测试任何东西。现在它工作了:)这个
    .readlines()
    位完全没有意义;迭代类似文件的对象,如