了解Popen.communicate

问题描述 投票:0回答:3

我有一个名为

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()
时,它永远挂起。

python subprocess
3个回答
75
投票

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

异常 EOFError 在子进程中由

raw_input()
引发(它期望数据但得到 EOF(无数据))。

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

为了避免死锁,您需要异步读/写(例如,通过使用线程或选择)或确切地知道读/写的时间和数量,例如

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

注意:如果读/写不同步,这是一个非常脆弱的代码;它陷入僵局。

谨防块缓冲问题(此处通过使用“-u”标志来解决,该标志关闭子进程中的stdin、stdout缓冲)。

bufsize=1
使管道在父端进行行缓冲


27
投票

请勿使用通信(input="")。它将输入写入进程,关闭其标准输入,然后读取所有输出。

这样做:

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]

但是由于communicate关闭了

stdout
stdin
stderr
,所以调用communicate后你就不能读或写了。


0
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.