代码之家  ›  专栏  ›  技术社区  ›  deft_code

逐行读取子进程stdout

  •  195
  • deft_code  · 技术社区  · 15 年前

    我的python脚本使用子进程调用一个非常嘈杂的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代码。

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


    更新:

    如果A 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()
    
    7 回复  |  直到 15 年前
        1
  •  151
  •   Rômulo Ceccon    15 年前

    我上次使用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 带有迭代器的解决方案应等效于使用 RealLoad() ,除了预读缓冲区,但是(或者正因为如此)所提议的更改确实为我产生了不同的结果(WindowsXP上的python 2.5)。

        2
  •  30
  •   jbg    9 年前

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

    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
    
        3
  •  16
  •   Steve Carter    11 年前

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

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

    变成

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

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

        4
  •  11
  •   nikoliazekter    9 年前

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

    bufsize=1, universal_newlines=True
    

    然后您可以像在示例中那样迭代。(用python 3.5测试)

        5
  •  1
  •   binariedMe    6 年前

    以下对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
    
        6
  •  0
  •   shakram02    7 年前

    我用蟒蛇3试过这个,它起作用了, 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()
    
        7
  •  0
  •   aiven    7 年前

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

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