了解 Popen.communicate

Posted

技术标签:

【中文标题】了解 Popen.communicate【英文标题】:Understanding Popen.communicate 【发布时间】:2013-05-22 00:31:27 【问题描述】:

我有一个名为 1st.py 的脚本,它创建了一个 REPL(读取评估打印循环):

print "Something to print"
while True:
    r = raw_input()
    if r == 'n':
        print "exiting"
        break
    else:
        print "continuing"

然后我使用以下代码启动1st.py

p = subprocess.Popen(["python","1st.py"], stdin=PIPE, stdout=PIPE)

然后尝试了这个:

print p.communicate()[0]

它失败了,提供了这个回溯:

Traceback (most recent call last):
  File "1st.py", line 3, in <module>
    r = raw_input()
EOFError: EOF when reading a line

你能解释一下这里发生了什么吗?当我使用p.stdout.read() 时,它会永远挂起。

【问题讨论】:

【参考方案1】:

您的第二段代码将第一段代码作为具有管道输入和输出的子进程开始。然后它关闭其输入并尝试读取其输出。

第一段代码尝试从标准输入中读取,但启动它的进程关闭了它的标准输入,因此它立即到达文件结尾,Python 将其变成异常。

【讨论】:

谢谢...但我只是不想关闭任何输入。我想在 1st.py 打印到其标准输出的任何地方打印。稍后,我将执行 p.stdin.write("Somthing to input") 然后 p.communicate()[0] 在第二个代码中打印它。你能帮助我吗?我只是不想关闭输入或输出。我是新手,所以请不要犹豫纠正我。谢谢【参考方案2】:

不要使用通信(输入=“”)。它将输入写入进程,关闭其标准输入,然后读取所有输出。

这样做:

p=subprocess.Popen(["python","1st.py"],stdin=PIPE,stdout=PIPE)

# get output from process "Something to print"
one_line_output = p.stdout.readline()

# write 'a line\n' to the process
p.stdin.write('a line\n')

# get output from process "not time to break"
one_line_output = p.stdout.readline() 

# write "n\n" to that process for if r=='n':
p.stdin.write('n\n') 

# read the last output from the process  "Exiting"
one_line_output = p.stdout.readline()

您将如何消除错误:

all_the_process_will_tell_you = p.communicate('all you will ever say to this process\nn\n')[0]

但是由于communication关闭了stdoutstdinstderr,调用communication之后就不能读写了。

【讨论】:

感谢您的回复..但是您可以看到我的第一个程序正在等待用户输入,在此之前它正在打印 "Something to print" 。所以我想要的是:从第二个过程中我只想阅读那一行(“要打印的东西”)。然后从这个(第二个进程)开始,我想写入它的标准输入,然后再次想读取第一个进程(子进程)正在写入标准输出的任何内容。那么,告诉我该怎么做? 我注释了代码。那现在应该做你想做的事。是不是更清楚了? 如果我在子进程等待用户输入时使用 p.stdout.read()/readline(),我的父进程会挂起(永远停止)。 你可以use "-u" flag and p.stdin.flush() (or bufsize=1) to fix the first example。对于第二个示例 +1(如果输入不依赖于输出,则更可取)。【参考方案3】:

.communicate() 写入输入(在这种情况下没有输入,所以它只是关闭子进程的标准输入以向子进程指示没有更多输入),读取所有输出,并等待子进程退出。

raw_input() 在子进程中引发异常 EOFError(它需要数据但得到 EOF(无数据))。

p.stdout.read() 永远挂起,因为它试图在孩子等待导致死锁的输入 (raw_input()) 的同时读取孩子的所有输出。

为避免死锁,您需要异步读/写(例如,通过使用线程或选择)或确切知道何时以及读/写多少,for example:

from subprocess import PIPE, Popen

p = Popen(["python", "-u", "1st.py"], stdin=PIPE, stdout=PIPE, bufsize=1)
print p.stdout.readline(), # read the first line
for i in range(10): # repeat several times to show that it works
    print >>p.stdin, i # write input
    p.stdin.flush() # not necessary in this case
    print p.stdout.readline(), # read output

print p.communicate("n\n")[0], # signal the child to exit,
                               # read the rest of the output, 
                               # wait for the child to exit

注意:如果读/写不同步,这是一个非常脆弱的代码;它死锁了。

注意block-buffering issue(这里使用"-u" flag that turns off buffering for stdin, stdout in the child解决)。

bufsize=1 makes the pipes line-buffered on the parent side.

【讨论】:

你能告诉我 print >>p.stdin,i 和 p.stdin.write(i) 有什么区别 这里printp.stdin.write(str(i)+"\n"); p.stdin.flush() 谢谢...还有一件事请告诉我这个 bufsize=1 是做什么的? Popen 中还有“-u”(["python", "-u", "1st.py"], stdin=PIPE, stdout=PIPE, bufsize=1) 我已经更新了答案。 Read the first link on why the buffering matters 我想不断地谈论一个进程,不断地向它发送命令并不断地接收它的响应。但这个过程总是存在的。如何做到这一点?

以上是关于了解 Popen.communicate的主要内容,如果未能解决你的问题,请参考以下文章

带有标准输入的 subprocess.Popen.communicate() 的管道损坏

python popen.communicate() 与多个标准输入写入

当 Popen.communicate() 还不够?

为啥 Popen.communicate() 返回 b'hi\n' 而不是 'hi'?

Unix Popen.communicate无法gzip大文件

Python Popen communicate 和wait使用上的区别