逐行读取子进程标准输出

时间:2010-05-10 16:47:32

标签: python subprocess

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

9 个答案:

答案 0 :(得分:159)

自从我上次使用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

当然,您仍然必须处理子进程'缓冲。

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

答案 1 :(得分:33)

参加聚会的时间已经很晚了,但我很惊讶没有看到我认为最简单的解决方案:

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

答案 2 :(得分:16)

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

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

成为

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

我在python中调用python时需要这个。

答案 3 :(得分:12)

您希望将这些额外参数传递给/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/

=CORREL(INDEX(A:A,1+$C$1):A100,B1:INDEX(B:B,100-$C$1))

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

答案 4 :(得分: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 != '':
    os.write(1, line)
  else:
    break

答案 5 :(得分:1)

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

答案 6 :(得分:0)

您还可以读取不带循环的行。在python3.6中工作。

import os
import subprocess

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

答案 7 :(得分:0)

该功能允许逐行同时并发地迭代stdoutstderr

如果需要同时获取stdoutstderr的输出流,可以使用以下函数。

该函数使用队列将两个Popen管道合并为一个迭代器。

在这里我们创建函数read_popen_pipes()

from queue import Queue, Empty
from concurrent.futures import ThreadPoolExecutor


def enqueue_output(file, queue):
    for line in iter(file.readline, ''):
        queue.put(line)
    file.close()


def read_popen_pipes(p):

    with ThreadPoolExecutor(2) as pool:
        q_stdout, q_stderr = Queue(), Queue()

        pool.submit(enqueue_output, p.stdout, q_stdout)
        pool.submit(enqueue_output, p.stderr, q_stderr)

        while True:
            out_line = err_line = ''

            try:
                out_line = q_stdout.get_nowait()
                err_line = q_stderr.get_nowait()
            except Empty:
                pass

            yield (out_line, err_line)

            if p.poll() is not None:
                break

read_popen_pipes()正在使用:

import subprocess as sp


with sp.Popen(my_cmd, stdout=sp.PIPE, stderr=sp.PIPE, text=True) as p:

    for out_line, err_line in read_popen_pipes(p):

        # Do stuff with each line, e.g.:
        print(out_line, end='')
        print(err_line, end='')

    return p.poll() # return status-code

答案 8 :(得分:-1)

Dunno(已添加到子流程模块中时),但对于Python 3,您可以使用proc.stdout.splitlines()

for line in proc.stdout.splitlines():
   print "stdout:", line