我有一个Python程序,即通过subprocess.Popen
打开另一个Python程序。第一个应该输出一些文本到控制台(仅用于信息),并将一些文本写入它产生的第二个程序。然后,它应该等待第二个程序响应(read()
)并打印该响应。
第二个应该听第一个的输入(通过raw_input()
),然后print
文本到第一个。
为了理解到底发生了什么,我把第二次延迟了5秒,结果让我感到有些惊讶。
以下是代码:
import subprocess
print "1st starting."
app = subprocess.Popen("name", shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE) #<--- B
print "Writing something to app's STDIN..."
app.stdin.write(some_text)
print "Reading something from my STDIN..." #<--- A
result = app.stdout.read()
print "Result:"
print result
对于第二个:
import time
print "app invoked."
print "Waiting for text from STDIN..."
text = raw_input()
#process(text)
time.sleep(5)
print "magic"
当我运行此代码时,它在A点暂停,因为这是最后一个控制台输出。
5秒后,将输出"Result:\n"
行,第2个程序print
编辑的所有内容都将显示在控制台中。
为什么第一个程序在读取第二个程序的stdout时会暂停?在读取输出之前是否必须等待其子节点终止?如何更改,以便我可以在程序之间传递消息?
我正在运行Debian Linux 7.0。
答案 0 :(得分:1)
您要求程序1读取程序2的输入。并且在输出任何内容之前,您将暂停程序2五秒钟。显然,程序1需要等待那五秒钟。所以会发生什么是完美的预期。
在读取输出之前是否必须等待其子节点终止?
在某种程度上,是的,因为输入和输出是缓冲的,所以有可能即使你在之后将延迟移动到,你也会发生相同的事情。
无论如何, raw_input()
会等待换行。
答案 1 :(得分:1)
答案不在于与子进程模块相关的任何魔术,而在于Python对象上read()
方法的典型行为。
如果你运行:
import subprocess
p = subprocess.Popen(['ls'], stdout=subprocess.PIPE)
help(p.stdout.read)
你会看到:
读(...)
read([size]) -> read at most size bytes, returned as a string. If the size argument is negative or omitted, read until EOF is reached. Notice that when in non-blocking mode, less data than what was requested may be returned, even if no size parameter was given.
(END)
同样的事情适用于所有类文件对象。这很简单:在没有参数的情况下调用read()
会消耗缓冲区,直到遇到错误(通常为EOF)。
EOF
在下列之前不会发送:
请注意os.read
具有不同的行为 - 更像是C中的典型缓冲I / O.内置的Python帮助功能是无用的,但是如果你在任何UNIXy系统上,你应该能够运行man 3 read
; Python行为或多或少与那里的行为相匹配。
上面的程序很好,但是这样的模式有时会导致死锁。子进程模块的文档警告了Popen.wait()的文档:
警告
当使用stdout = PIPE和/或stderr = PIPE并且子进程为管道生成足够的输出以阻止等待OS管道缓冲区接受更多数据时,这将会死锁。使用communic()来避免这种情况。
如果在与子进程进行双向通信时不小心,可能会遇到类似情况,具体取决于子进程正在执行的操作。
修改强>:
顺便说一句,this page涵盖了EOF管道的行为:
如果引用管道写入端的所有文件描述符都已存在 关闭,然后尝试从管道读取(2)将看到文件结束 (read(2)将返回0)。
编辑2 :
正如Lennart所述,如果你想要真正的双向通信超越一次性写入,你还需要提防缓冲。如果您阅读this,您将会对它有所了解,但您应该知道这是缓冲IO几乎总是在基于UNIX的系统中工作的方式 - 它不是Python的怪癖。运行man stdio.h
以获取更多信息。