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

Python:subprocess32 process.stdout.readline()等待时间,python,python-2.7,subprocess,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.ST

如果我使用例如“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(),run函数中的打印确实会执行

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

注意:我刚刚发现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

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

print "hello world" + str(i)
sys.stdout.flush()
当标准输出是一个终端时,标准输出是行缓冲的。但如果不是,则stdout是块缓冲的,您需要显式地刷新它

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

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

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


一种解决方案可能是模拟一个伪终端,向程序提供“提示”,让它们以行缓冲模式运行。尽管如此,这并不是一个在所有情况下都有效的解决方案。

对于python以外的其他东西,您可以尝试使用:

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

正常情况下,unbuffer不会从stdin读取数据。这简化了在某些情况下使用unbuffer。要在管道中使用unbuffer,请使用-p标志。例子: 流程1 |解除缓冲-p流程2 |流程3

因此,在你的情况下:

run(["unbuffer",cmd]) 

文件中列出了一些注意事项,但这是另一种选择

在大多数系统上,命令行根据stdout是终端还是管道对行缓冲区或块缓冲区进行编程。在unixy系统上,父进程可以创建一个伪终端,以获得类似终端的行为,即使子进程不是真正从终端运行。您可以使用
pty
模块来创建伪终端,也可以使用
pexpect
模块来简化对交互式程序的访问

如注释中所述,使用
poll
读取行可能会导致数据丢失。一个例子是进程终止时留在标准输出管道中的数据。读取
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()

即使使用它,我仍然得到相同的结果。注意:我将subprocess32与Python2.7.10process=subprocess.Popen(命令,stdout=subprocess.PIPE,stderr=subprocess.stdout)一起使用,并通过以下调用从进程中读取:while process.poll()==None:print process.stdout.readline()就像上面的例子一样。ahhh:)而不是执行打印过程。我对iter中的行(process.stdout.readline,b“”)执行了stdout.readline(),但读取行直到刷新缓冲区后才返回。根据stdout是终端还是管道,过程倾向于使用不同的缓冲区。您正在使用管道,因此子级将阻止缓冲区。请尝试使用
pty
模块中的伪终端,或使用为此类对象构建的
pexpect
。1
命令应该是POSIX上的列表(例如,
“ls-Rlah/”.split()
)2。使用
is None
代替
==None
3<代码>打印行
将换行符加倍,使用
打印行
(注意:逗号)来抑制第二个不必要的换行符4<代码>如果p.poll():p.kill()错误。看,我不知道,没有终端,它是块缓冲,有用的信息,谢谢!然而,我要寻找的是,有没有一种方法可以在非块缓冲区模式下调用子流程?谢谢,是的,这很有效!但是安德里亚,有没有更通用的方法?i、 e.如果进程不是python,那么我将被卡住。@Har:这适用于python在父进程中完成的缓冲。它确实(也不能)影响子流程。我们所说的那种缓冲完全是由程序本身或它所使用的库来完成的,内核在这里没有任何作用(因此我们不能将缓冲区大小从一个进程“转移”到另一个进程),在代码中乱丢垃圾很少是正确的答案,甚至在您无法控制的代码上也不可能。大多数程序应该只写标准输出,让父程序控制它的行或块是否被缓冲。除了在Windows上,Microsoft从未提供过好的pty解决方案。不要使用
.poll()
(这样最终可能会丢失数据)。看见无关:如果
process.poll()。不要使用
.poll()==None
,如果您需要与
None
进行比较,请使用
.poll()为None
,您是对的!我尽可能地保留了原始代码,但在您的评论之后,我意识到我最好给出一个更完整的示例。谢谢。使用
stdin=s
run(["unbuffer",cmd]) 
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()