关于管道stdio和subprocess.Popen

时间:2013-04-11 10:46:50

标签: python linux shell stdout stdin

我有一个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。

2 个答案:

答案 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在下列之前不会发送:

  • 子进程调用sys.stdout.close()或
  • 子进程退出,Python运行时和/或OS内核清理其文件描述符

请注意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以获取更多信息。