我想编写一个执行外部脚本(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())
Run Code Online (Sandbox Code Playgroud)
py
import sys
for line in sys.stdin:
print(line)
Run Code Online (Sandbox Code Playgroud)
输出应为:
>>> b'hello'
>>> b'hello'
>>> b'hello'
Run Code Online (Sandbox Code Playgroud)
问题是A只是在等待
print(process.stdout.read())
Run Code Online (Sandbox Code Playgroud)
如果我通过添加close()来修改A:
for _ in range(3):
process.stdin.write(b'hello')
process.stdin.close()
print(process.stdout.read())
Run Code Online (Sandbox Code Playgroud)
我得到:
>>> 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
Run Code Online (Sandbox Code Playgroud)
communicate()Python已经communicate()实现了方法(转到A.py,B.py就可以了)。但是,如果您需要更复杂的数据,则它仅适用于简单的通信(您知道要先发送哪些数据):
send data to process B
read stdout
if stdout ...
do something bases on stdout
write to stdin
Run Code Online (Sandbox Code Playgroud)
您必须在这里实现自己的communicate()原始实现。
我已经一步一步地测试和调试了,这是发生了什么:
# For Popen(bufsize!=0)
A: process.stdin.write(b'hello\r\n')
B: line = sys.stdin.readline() # Hangs
Run Code Online (Sandbox Code Playgroud)
所以添加后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
Run Code Online (Sandbox Code Playgroud)
那有什么用呢?
# 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()
Run Code Online (Sandbox Code Playgroud)
您已将缓冲设置为io.DEFAULT_BUFFER_SIZE(通常为4090B)。从文档:
在创建stdin / stdout / stderr管道文件对象时,bufsize将作为io.open()函数的相应参数提供:0表示未缓冲(读和写是一个系统调用,并且可以返回short),1表示行缓冲,任何其他正值表示使用大约该大小的缓冲区。负bufsize(默认值)表示将使用io.DEFAULT_BUFFER_SIZE的系统默认值。
因此,起初A不会刷新,因为它尚未填满缓冲区,因此B正在等待。它不可能简单地 process.stdin.flush()在Windows下,所以你必须使用bufsize=0。
由于的缘故,写os.linesep(\r\n)也很重要readline()。
注意:我相信它也应该与bufsize=1(行缓冲)一起使用,但是没有。我不知道为什么。
然后,B当它不会刷新时,也会发生同样的事情,sys.stdout这让我感到惊讶,它B:sys.stdout没有设置为无缓冲的,因为:
创建stdin / stdout / stderr管道文件对象时,bufsize将作为io.open()函数的相应参数提供。
无论如何,你必须调用sys.stdout.flush()在B。
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())
Run Code Online (Sandbox Code Playgroud)
B.py:
import sys
for line in sys.stdin:
print('Send back', line.strip())
sys.stdout.flush()
Run Code Online (Sandbox Code Playgroud)