如何从subprocess.Popen()获取输出?proc.stdout.readline()阻塞,无数据显示

41 投票
4 回答
97909 浏览
提问于 2025-04-15 14:07

我想从执行 Test_Pipe.py 这个文件中获取输出,我在 Linux 上尝试了以下代码,但没有成功。

Test_Pipe.py

import time
while True :
    print "Someting ..."
    time.sleep(.1)

Caller.py

import subprocess as subp
import time

proc = subp.Popen(["python", "Test_Pipe.py"], stdout=subp.PIPE, stdin=subp.PIPE)

while True :
    data = proc.stdout.readline() #block / wait
    print data
    time.sleep(.1)

那行 proc.stdout.readline() 被阻塞了,所以没有任何数据输出。

4 个回答

16

Test_Pipe.py 默认会把它的输出缓存起来,这样在 Caller.py 中的 proc 就看不到任何输出,直到子进程的缓存满了(如果缓存大小是8KB,那么大概需要一分钟才能填满 Test_Pipe.py 的输出缓存)。

如果想让输出不缓存(对于文本流来说是逐行缓存),你可以给子Python脚本传递一个 -u 标志。这样就可以实时逐行读取子进程的输出:

import sys
from subprocess import Popen, PIPE

proc = Popen([sys.executable, "-u", "Test_Pipe.py"], stdout=PIPE, bufsize=1)
for line in iter(proc.stdout.readline, b''):
    print line,
proc.communicate()

有关如何解决非Python子进程的块缓存问题,请查看 Python: read streaming input from subprocess.communicate() 中的链接。

20

Nadia的代码确实可以用,但用1字节的缓冲区来调用read是不太推荐的。更好的做法是使用fcntl把标准输出的文件描述符设置为非阻塞模式。

fcntl.fcntl(
    proc.stdout.fileno(),
    fcntl.F_SETFL,
    fcntl.fcntl(proc.stdout.fileno(), fcntl.F_GETFL) | os.O_NONBLOCK,
)

然后可以用select来检查数据是否准备好了。

while proc.poll() == None:
    readx = select.select([proc.stdout.fileno()], [], [])[0]
    if readx:
        chunk = proc.stdout.read()
        print chunk

她说得对,你的问题肯定和你发的Caller.py以及Test_Pipe.py不一样,因为这两个文件按原样提供是可以正常工作的。

46

你当然可以使用subprocess.communicate这个方法,但我觉得你想要的是实时的输入和输出。

readline被阻塞了,因为这个进程可能在等你的输入。你可以通过逐个字符读取来解决这个问题,像下面这样:

import subprocess
import sys

process = subprocess.Popen(
    cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

while True:
    out = process.stdout.read(1)
    if out == '' and process.poll() != None:
        break
    if out != '':
        sys.stdout.write(out)
        sys.stdout.flush()

撰写回答