首页 文章

逐行读取子进程标准输出

提问于
浏览
190

我的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代码进行排序 .

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


Update:

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


Update 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()

6 回答

  • 30

    自从我上次使用Python以来已经很长时间了,但我认为问题在于 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 line != '':
        #the real code does filtering here
        print "test:", line.rstrip()
      else:
        break
    

    当然,你仍然需要处理子进程的缓冲 .

    注意:带有迭代器的解决方案应该等效于使用 readline() ,除了预读缓冲区,但是(或者正是因为这个)建议的更改确实为我产生了不同的结果(Windows XP上的Python 2.5) .

  • 10

    派对迟到了,但很惊讶没有看到我认为这里最简单的解决方案:

    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
    
  • 146

    实际上,如果您整理了迭代器,那么缓冲现在可能是您的问题 . 您可以告诉子进程中的python不要缓冲其输出 .

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

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

    从python中调用python时我需要这个 .

  • 0

    您想将这些额外参数传递给 subprocess.Popen

    bufsize=1, universal_newlines=True
    

    然后你可以像你的例子一样迭代 . (使用Python 3.5测试)

  • 1

    以下对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 != b'':
        os.write(1, line)
      else:
        break
    
  • 16

    我用python3尝试了这个并且它有效,source

    def output_reader(proc):
        for line in iter(proc.stdout.readline, b''):
            print('got line: {0}'.format(line.decode('utf-8')), end='')
    
    
    def main():
        proc = subprocess.Popen(['python', 'fake_utility.py'],
                                stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
    
        t = threading.Thread(target=output_reader, args=(proc,))
        t.start()
    
        try:
            time.sleep(0.2)
            import time
            i = 0
    
            while True:
            print (hex(i)*512)
            i += 1
            time.sleep(0.5)
        finally:
            proc.terminate()
            try:
                proc.wait(timeout=0.2)
                print('== subprocess exited with rc =', proc.returncode)
            except subprocess.TimeoutExpired:
                print('subprocess did not terminate in time')
        t.join()
    

相关问题