使用stdin和stdout在2个进程之间进行通信

时间:2014-10-20 11:17:26

标签: python subprocess

我想编写一个执行外部脚本(B)的简单脚本(A)

  • A应该通过写入stdin和阅读stdout来与B通信
  • B应该读取它的标准输入并打印它

所有这些都应该在不关闭流的情况下完成

A.py

import subprocess
process = subprocess.Popen(['python', 'B.py'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
for _ in range(3):
    process.stdin.write(b'hello')
    print(process.stdout.read())

B.py

import sys

for line in sys.stdin:
    print(line)

输出应为:

>>> b'hello'
>>> b'hello'
>>> b'hello'

问题是A只是等待

print(process.stdout.read())

如果我通过添加close():

来修改A.
for _ in range(3):
    process.stdin.write(b'hello')
    process.stdin.close()
    print(process.stdout.read())

我明白了:

>>> b'hello\n'
>>> Traceback (most recent call last):
>>>   File "A.py", line 7, in <module>
>>>     process.stdin.write(b'hello')
>>> ValueError: write to closed file

1 个答案:

答案 0 :(得分:3)

使用communicate()

Python已经实现了communicate()方法(转到A.pyB.py很好)。然而,它只适合简单的通信(你知道你要预先发送什么数据),如果你需要一个更复杂的通信,如:

send data to process B
read stdout
if stdout ...
    do something bases on stdout
    write to stdin

您必须实施自己的communicate()原始实施here


一步一步

我已逐步测试和调试了这一步,以下是:

# For Popen(bufsize!=0)
A: process.stdin.write(b'hello\r\n')
B: line = sys.stdin.readline() # Hangs

添加bufsize=0(无缓冲)

之后
# Popen(bufsize=0)
A: process.stdin.write(b'hello\r\n') # Without \r\n B still hangs
B: line = sys.stdin.readline()
B: print('Send back', line.strip()) # Without strip it prints empty line
A: process.stdout.readline() # Hangs

那么什么有用?

# Popen(bufsize=0)
A: process.stdin.write(b'hello\r\n')
B: line = sys.stdin.readline()
B: print('Send back', line.strip())
B: sys.stdout.flush()
A: process.stdout.readline()

解释

您已将缓冲设置为io.DEFAULT_BUFFER_SIZE(通常为4090B)。来自docs

  在创建stdin / stdout / stderr管道文件对象时,

bufsize 将作为io.open()函数的相应参数提供:0表示无缓冲(读取和写入是一个系统调用,可以返回short),1表示行缓冲,任何其他正值表示使用大约该大小的缓冲区。负bufsize(默认值)表示将使用系统默认值io.DEFAULT_BUFFER_SIZE。

因此,首先A将不会刷新,因为它尚未填充其缓冲区,因此B正在等待。在Windows下是not possible to simply process.stdin.flush(),因此您必须使用bufsize=0

由于readline(),写os.linesep (\r\n)也很重要。

注意: 我相信它应该也适用于bufsize=1(行缓冲),但事实并非如此。我不明白为什么。

然后,当B无法刷新sys.stdout时,同样的情况也会发生,令我感到惊讶的是,B:sys.stdout未设置为无缓冲,因为:< / p>

  在创建stdin / stdout / stderr管道文件对象时,

bufsize 将作为io.open()函数的相应参数提供

无论如何,您必须在sys.stdout.flush()中致电B

它与close()一起使用,因为它会强制flush()


Gimme teh code

<强> A.py

import subprocess
import sys

process = subprocess.Popen([sys.executable, r'B.py'], stdin=subprocess.PIPE, 
                            stdout=subprocess.PIPE, bufsize=0)
for _ in range(3):
    process.stdin.write(b'hello\r\n')
    print(process.stdout.readline())

<强> B.py

import sys

for line in sys.stdin:
    print('Send back', line.strip())
    sys.stdout.flush()