process.stdout.readline() 挂起了。如何正确使用?

5 投票
3 回答
14316 浏览
提问于 2025-04-18 12:32

我想要反复发送请求来处理标准输入,并从标准输出接收响应,而不是多次调用 subprocess。我可以通过 p.communicate 实现一次请求-响应的循环,但如果不想多次调用 subprocess,我需要使用 process.stdout.readline(),但这会导致程序挂起。请问该如何正确使用它?我使用的是 Python 2.7 64位,Windows 7。提前谢谢!

main.py:

import subprocess
p = subprocess.Popen(['python','subproc.py'],stdin=subprocess.PIPE,stdout=subprocess.PIPE)

while True:
    s=raw_input('Enter message:')
    p.stdin.write(s)
    p.stdin.flush()
    response = p.stdout.readline()
    if response!= '':
        print "Process response:", response
    else:
        break

subproc.py:

from __future__ import division
import pyximport
s=raw_input()
print 'Input=',s

3 个回答

-5

你需要使用 communicate 来和你的子进程进行互动(可以查看这个链接了解更多:https://docs.python.org/2/library/subprocess.html#subprocess.Popen.communicate)。下面是你主代码的一个更新版本:

import subprocess
p = subprocess.Popen(['python','subproc.py'],stdin=subprocess.PIPE,stdout=subprocess.PIPE)

while True:
    s = raw_input('Enter message:')
    response, _ = p.communicate(s)
    if response!= '':
        print "Process response:", response
    else:
        break

另外,你要注意的是,虽然你的主代码里有一个循环,但子进程的代码只会运行一次。只有第一次循环能正确接收到响应,第二次调用 communicate 时会出现错误,因为到那时子进程的 stdin 文件已经关闭了。

5

不能指望子进程会立刻收到你发送到它标准输入(stdin)的所有数据,因为在这个过程中可能会有缓冲区的干扰。如果你需要保持文件打开以便继续输出,至少应该调用它的 flush() 方法来确保数据被发送出去。

另外,也不能指望子进程的输出会马上可以读取。如果它没有刷新(flush)或关闭它的输出流,那么输出的结束符(EOL)可能会被缓冲,这样你如果不采取措施让子进程继续工作,你的 readline() 可能会一直等待下去。这时候你的程序就无法继续,因为它被卡在 readline() 里。如果子进程是为这种情况设计的,可能会正常工作,但如果不是,你就需要使用更安全的方法,比如 subprocess.communicate()

正如你所观察到的,不能对同一个子进程多次调用 communicate()。这是符合它文档中说明的:它会读取所有输出直到文件结束,并等待子进程结束。如果你想在这种模式下发送多个输入,应该把所有输入拼成一个字符串,传给 communicate(),然后再读取所有的回答。

另外,如果你确实需要在写入和读取之间交替进行,而你的子进程又没有特别设计来处理这种情况,那么更安全的做法是将读取和写入放在不同的线程中,且不指望写入和读取能够完美交错。

10

要让这个程序正常工作,你可以做几个小调整。首先,在子进程中使用 -u 选项来关闭缓冲输出。其次,要在用户输入的消息后面加上一个换行符,这样子进程中的 raw_input 调用才能完成。

main.py

import subprocess

# We use the -u option to tell Python to use unbuffered output
p = subprocess.Popen(['python','-u', 'subproc.py'],
                     stdin=subprocess.PIPE,
                     stdout=subprocess.PIPE)

while True:
    s = raw_input('Enter message:')
    p.stdin.write(s + "\n")  # Include '\n'
    p.stdin.flush()
    response = p.stdout.readline()
    if response != '': 
        print "Process response:", response
    else:
        break

你还应该把子进程放在一个无限循环里,不然在发送第一条消息后,程序就会出问题:

subproc.py:

while True:
    s = raw_input()
    print 'Input=',s

输出:

dan@dantop:~$ ./main.py 
Enter message:asdf
Process response: Input= asdf

Enter message:asdf
Process response: Input= asdf

Enter message:blah blah
Process response: Input= blah blah

Enter message:ok
Process response: Input= ok

撰写回答