逐行读取子进程标准输出

新手上路,请多包涵

我的 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()

原文由 deft_code 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 446
2 个回答

我认为问题在于语句 for line in proc.stdout ,它在迭代之前读取整个输入。解决方案是使用 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)。

原文由 Rômulo Ceccon 发布,翻译遵循 CC BY-SA 4.0 许可协议

聚会有点晚了,但很惊讶没有看到我认为这里最简单的解决方案:

 import io
import subprocess

proc = subprocess.Popen(["prog", "arg"], stdout=subprocess.PIPE)
for line in io.TextIOWrapper(proc.stdout, encoding="utf-8"):  # or another encoding
    # do something with line

(这需要 Python 3。)

原文由 jbg 发布,翻译遵循 CC BY-SA 4.0 许可协议

推荐问题