我的python脚本使用subprocess调用一个非常吵闹的linux实用程序。我想将所有输出存储到一个日志文件中,并将其中一些显示给用户。我认为下面的代码可以工作,但是直到实用程序产生大量输出,输出才显示在我的应用程序中。

#fake_utility.py, just generates lots of output over time
import time
i = 0
while True:
   print hex(i)*512
   i += 1
   time.sleep(0.5)

#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
for line in proc.stdout:
   #the real code does filtering here
   print "test:", line.rstrip()

我真正想要的行为是让过滤器脚本打印从子进程接收到的每一行。有点像tee,但用的是python代码。

我错过了什么?这可能吗?


更新:

如果将sys.stdout.flush()添加到fake_utility.py中,代码在python 3.1中具有所需的行为。我使用的是python 2.6。您可能认为使用proc.stdout.xreadlines()的工作方式与py3k相同,但事实并非如此。


更新2:

下面是最小的工作代码。

#fake_utility.py, just generates lots of output over time
import sys, time
for i in range(10):
   print i
   sys.stdout.flush()
   time.sleep(0.5)

#display out put line by line
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
#works in python 3.0+
#for line in proc.stdout:
for line in iter(proc.stdout.readline,''):
   print line.rstrip()

当前回答

您需要将这些额外的参数传递给子进程。Popen:

bufsize=1, universal_newlines=True

然后可以像示例中那样进行迭代。(使用Python 3.5测试)

其他回答

我认为问题在于proc.stdout中的for line语句,它在迭代整个输入之前读取它。解决方案是使用readline()代替:

#filters output
import subprocess
proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)
while True:
  line = proc.stdout.readline()
  if not line:
    break
  #the real code does filtering here
  print "test:", line.rstrip()

当然,您仍然需要处理子进程的“缓冲”。

注意:根据文档,使用迭代器的解决方案应该等效于使用readline(),除了预读缓冲区,但(或正因为如此)建议的更改确实为我产生了不同的结果(Windows XP上的Python 2.5)。

实际上,如果您对迭代器进行了排序,那么缓冲现在可能是您的问题。你可以告诉子进程中的python不要缓冲它的输出。

proc = subprocess.Popen(['python','fake_utility.py'],stdout=subprocess.PIPE)

就变成了

proc = subprocess.Popen(['python','-u', 'fake_utility.py'],stdout=subprocess.PIPE)

当从python内部调用python时,我需要这个。

以下对Rômulo的回答的修改适用于Python 2和3(2.7.12和3.6.1):

import os
import subprocess

process = subprocess.Popen(command, stdout=subprocess.PIPE)
while True:
  line = process.stdout.readline()
  if line != '':
    os.write(1, line)
  else:
    break

您需要将这些额外的参数传递给子进程。Popen:

bufsize=1, universal_newlines=True

然后可以像示例中那样进行迭代。(使用Python 3.5测试)

你也可以读行w/o循环。适用于python3.6。

import os
import subprocess

process = subprocess.Popen(command, stdout=subprocess.PIPE)
list_of_byte_strings = process.stdout.readlines()