如何从 subprocess.Popen() 获取输出。 proc.stdout.readline() 块,没有数据打印出来

Posted

技术标签:

【中文标题】如何从 subprocess.Popen() 获取输出。 proc.stdout.readline() 块,没有数据打印出来【英文标题】:How to get output from subprocess.Popen(). proc.stdout.readline() blocks, no data prints out 【发布时间】:2010-11-26 04:50:48 【问题描述】:

我想要执行 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() 行被阻塞,所以没有数据打印出来。

【问题讨论】:

重复:***.com/search?q=%5Bpython%5D+subprocess+output、***.com/questions/803265/…、***.com/questions/1277866/… 【参考方案1】:

您显然可以使用 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()

【讨论】:

you don't need process.poll() in this case.【参考方案2】:

Nadia 的 sn-p 确实有效,但非常不推荐使用 1 字节缓冲区调用 read。更好的方法是使用 fcntl 将 stdout 文件描述符设置为非阻塞

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 所提供的不同。

https://derrickpetzold.com/p/capturing-output-from-ffmpeg-python/

【讨论】:

由于the block-buffering issue. See my answer,它不会比问题中的代码更快地产生输出。 代码确实会产生实时输出。你有问题吗? 点击评论中的链接。 数据“准备就绪”是什么意思?是换行还是至少一个字节的数据准备好了(即使此类数据的生产者仍在写入其标准输出)? @me,J.F.Sebastian 下面的回答解决了这个问题。【参考方案3】:

Test_Pipe.py 默认缓冲其标准输出,因此 Caller.py 中的 proc 在子缓冲区已满之前看不到任何输出(如果缓冲区大小为 8KB,则填充 Test_Pipe.py 大约需要一分钟' s 标准输出缓冲区)。

要使输出无缓冲(文本流的行缓冲),您可以将 -u flag 传递给子 Python 脚本。它允许“实时”逐行读取子进程的输出:

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: read streaming input from subprocess.communicate() 中的链接,了解如何解决非 Python 子进程的块缓冲问题。

【讨论】:

再次使用 1 字节缓冲区,这非常低效。 @DerrickPetzold:错了。 bufsize=1 表示“行缓冲”。它使用与bufsize=-1 相同的缓冲区大小。如果您实际运行代码,您可能会发现您的两个 cmets 都是错误的(比较时间性能并测量读取第一个字节之前的时间) 我喜欢这种方式,因为您可以直接打印到屏幕上,还可以存储 popen 的结果以做某事,然后检查错误代码等...竖起大拇指 =) 我在没有 bufsize 的情况下运行它,并且在我的代码中对我来说工作正常。【参考方案4】:

为了避免诸如“实时获取子进程的输出到主进程”之类的任务缓冲经常出现的许多问题,我始终建议对所有非 Windows 平台使用pexpect,wexpect on Windows,而不是 subprocess,当需要此类任务时。

【讨论】:

以上是关于如何从 subprocess.Popen() 获取输出。 proc.stdout.readline() 块,没有数据打印出来的主要内容,如果未能解决你的问题,请参考以下文章

如何从 subprocess.Popen 使用 STDIN [重复]

Python:使用 subprocess.call 获取输出,而不是 Popen [重复]

subprocess.Popen 和缓冲的进程输出

将标准输出从 subprocess.Popen 保存到文件,并将更多内容写入文件

实时 subprocess.Popen 通过 stdout 和 PIPE

subprocess.Popen,从子进程(子)中获取变量[重复]