使用标准输入和标准输出在 2 个进程之间进行通信
Posted
技术标签:
【中文标题】使用标准输入和标准输出在 2 个进程之间进行通信【英文标题】:comunicate between 2 processes with stdin and stdout 【发布时间】:2014-10-20 11:17:26 【问题描述】:我想编写一个执行外部脚本 (B) 的简单脚本 (A)
A 应该通过写入其标准输入并读取其标准输出来与 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())
如果我修改 A,通过添加 close():
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】:使用communicate()
Python 已经实现了communicate()
方法(它转到A.py
,B.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:
bufsize 将在创建 stdin/stdout/stderr 管道文件对象时作为 io.open() 函数的相应参数提供:0 表示无缓冲(读取和写入是一个系统调用,并且可以返回短),1 表示行缓冲,任何其他正值表示使用大约该大小的缓冲区。负 bufsize(默认值)表示将使用系统默认值 io.DEFAULT_BUFFER_SIZE。
所以一开始A
不会刷新,因为它还没有填满缓冲区,因此B
正在等待。 Windows下是not possible to simply process.stdin.flush()
,所以你必须使用bufsize=0
。
另外写os.linesep
(\r\n
) 也很重要,因为readline()
。
注意:我相信它应该也适用于 bufsize=1
(行缓冲),但它没有。我不知道为什么。
同样的情况发生在B
,它不会刷新sys.stdout
,令我惊讶的是,B:sys.stdout
没有设置为无缓冲,因为:
bufsize 将在创建 stdin/stdout/stderr 管道文件对象时作为 io.open() 函数的相应参数提供
无论如何,您必须在B
中致电sys.stdout.flush()
。
它适用于close()
,因为它强制flush()
。
给我代码
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()
【讨论】:
添加 \n 后它仍然像以前一样等待 尝试添加\r\n,未成功。添加刷新时,我在第二次迭代时收到此错误:IOError: [Errno 22] Invalid argument 哇,很好的答案,也适用于我,只需稍作改动,我在 A 中写入后刷新,而在 B 中根本不刷新。我用的是Python3.2,这有区别吗? @OferHelman 我正在使用 Windows 7 运行Python 3.2.5 (default, May 15 2013, 23:07:10) [MSC v.1500 64 bit (AMD64)] on win32
,我认为它可能是特定于版本的。我觉得让这些东西正常工作有点神奇。
有趣,与我的设置非常相似,我在您的答案中添加了适合我的排列以上是关于使用标准输入和标准输出在 2 个进程之间进行通信的主要内容,如果未能解决你的问题,请参考以下文章