Python:subprocess32 process.stdout.readline()等待时间

时间:2015-12-26 19:34:51

标签: python python-2.7 subprocess

如果我使用例如“ls -Rlah /”运行以下函数“run”,我会立即通过print语句获得输出

import subprocess32 as subprocess
def run(command):
    process = subprocess.Popen(command,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
    try:
        while process.poll() == None:
            print process.stdout.readline()
    finally:
        # Handle the scenario if the parent
        # process has terminated before this subprocess
        if process.poll():
            process.kill()

但是,如果我使用下面的python示例程序,它似乎会停留在process.poll()或process.stdout.readline()上,直到程序完成。我认为它是stdout.readline(),因为如果我将输出的字符串数量从10增加到10000(在示例程序中)或者在每次打印后添加到sys.stdout.flush()中,则在运行中打印函数确实被执行了。

如何使子流程的输出更加实时?

注意:我刚刚发现python示例程序在输出时没有执行sys.stdout.flush(),是否有办法让子进程的调用者以某种方式强制执行此操作?

每5秒输出10个字符串的示例程序。

#!/bin/env python
import time

if __name__ == "__main__":

    i = 0
    start = time.time()
    while True:
        if time.time() - start >= 5:
            for _ in range(10):
                print "hello world" + str(i)
            start = time.time()
            i += 1
        if i >= 3:
            break

3 个答案:

答案 0 :(得分:3)

在大多数系统上,命令行程序行缓冲区或块缓冲区取决于stdout是终端还是管道。在unixy系统上,父进程可以创建一个伪终端,以获得类似终端的行为,即使孩子并非真正从终端运行。您可以使用pty模块创建伪终端或使用pexpect模块,以便轻松访问交互式程序。

如评论中所述,使用poll读取行可能会导致数据丢失。一个例子是当进程终止时在stdout管道中留下的数据。阅读pty与管道有点不同,当您关闭子项以使其全部正常工作时,您会发现需要捕获IOError,如下例所示。

try:
    import subprocess32 as subprocess
except ImportError:
    import subprocess
import pty
import sys
import os
import time
import errno

print("running %s" % sys.argv[1])

m,s = (os.fdopen(pipe) for pipe in pty.openpty())
process = subprocess.Popen([sys.argv[1]],
                           stdin=s,
                           stdout=s,
                           stderr=subprocess.STDOUT)
s.close()

try:
    graceful = False
    while True:
        line = m.readline()
        print line.rstrip()
except IOError, e:
    if e.errno != errno.EIO:
        raise
    graceful = True
finally:
    # Handle the scenario if the parent
    # process has terminated before this subprocess
    m.close()
    if not graceful:
        process.kill()
    process.wait()

答案 1 :(得分:2)

您应该在脚本中刷新标准输出:

print "hello world" + str(i)
sys.stdout.flush()

当标准输出是终端时,stdout是行缓冲的。但是当它不是时,stdout是块缓冲的,你需要明确地刷新它。

如果您无法更改脚本源,可以使用Python的-u选项(在子流程中):

-u     Force stdin, stdout and stderr to be totally unbuffered. 

您的命令应该是:['python', '-u', 'script.py']

通常,这种缓冲发生在用户空间中。没有通用的方法来强制应用程序刷新其缓冲区:某些应用程序支持命令行选项(如Python),其他应用程序支持信号,其他应用程序不支持任何内容。

一种解决方案可能是模拟伪终端,提供"提示"他们应该在行缓冲模式下运行的程序。尽管如此,这并不是一个适用于所有情况的解决方案。

答案 2 :(得分:2)

对于python之外的其他内容,您可以尝试使用unbuffer

  

unbuffer禁用从非交互式程序重定向程序输出时发生的输出缓冲。例如,假设您通过运行od然后更多来查看fifo的输出。       od -c / tmp / fifo |更多   在生成整页输出之前,您将看不到任何内容。   您可以按如下方式禁用此自动缓冲:

unbuffer od -c /tmp/fifo | more
  

通常,unbuffer不会从stdin读取。这在某些情况下简化了unbuffer的使用。要在管道中使用unbuffer,请使用-p标志。例:   process1 | unbuffer -p process2 | process3

所以在你的情况下:

run(["unbuffer",cmd]) 

文档中列出了一些警告,但这是另一种选择。