【问题标题】:comunicate between 2 processes with stdin and stdout使用标准输入和标准输出在 2 个进程之间进行通信
【发布时间】: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

【问题讨论】:

    标签: python subprocess


    【解决方案1】:

    使用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

    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,我认为它可能是特定于版本的。我觉得让这些东西正常工作有点神奇。
    • 有趣,与我的设置非常相似,我在您的答案中添加了适合我的排列
    最近更新 更多