如何从子进程中获取“实时”信息.Popen在python(2.5)中

时间:2009-05-17 15:20:30

标签: python subprocess stdout popen

我想以下列方式使用子进程模块:

  1. 创建一个可能需要很长时间才能执行的新流程。
  2. 捕获stdout(或stderr,或两者一起或分别捕获)
  3. 处理来自子进程的数据,可能会在收到的每一行上触发事件(在wxPython中说)或者只是暂时将它们打印出来。
  4. 我已经使用Popen创建了进程,但是如果我使用communication(),那么一旦进程终止,数据就会立刻出现在我面前。

    如果我创建了一个阻塞readline() myprocess.stdout的单独线程(使用stdout = subprocess.PIPE),我也不会使用此方法获得任何行,直到进程终止。 (无论我设置为bufsize)

    有没有办法处理这个并不可怕,并且在多个平台上运行良好?

10 个答案:

答案 0 :(得分:8)

使用似乎无效的代码进行更新(无论如何在Windows上)

class ThreadWorker(threading.Thread):
    def __init__(self, callable, *args, **kwargs):
        super(ThreadWorker, self).__init__()
        self.callable = callable
        self.args = args
        self.kwargs = kwargs
        self.setDaemon(True)

    def run(self):
        try:
            self.callable(*self.args, **self.kwargs)
        except wx.PyDeadObjectError:
            pass
        except Exception, e:
            print e



if __name__ == "__main__":
    import os
    from subprocess import Popen, PIPE

    def worker(pipe):
        while True:
            line = pipe.readline()
            if line == '': break
            else: print line

    proc = Popen("python subprocess_test.py", shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)

    stdout_worker = ThreadWorker(worker, proc.stdout)
    stderr_worker = ThreadWorker(worker, proc.stderr)
    stdout_worker.start()
    stderr_worker.start()
    while True: pass

答案 1 :(得分:7)

stdout将被缓冲 - 因此在填充缓冲区或子进程退出之前,您将无法获得任何内容。

您可以尝试从子进程刷新stdout,或使用stderr,或在非缓冲模式下更改stdout。

答案 2 :(得分:3)

这对我有用:

cmd = ["./tester_script.bash"]
p = subprocess.Popen( cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
while p.poll() is None:
    out = p.stdout.readline()
    do_something_with( out, err )

在您的情况下,您可以尝试将对子流程的引用传递给您的工作线程,并在线程内部进行轮询。我不知道当两个线程轮询(并与之交互)同一个子进程时它会如何表现,但它可能会起作用。

另请注意,while p.poll() is None:是按原样使用的。 将其替换为while not p.poll(),如python 0(成功终止的返回码)也被视为False

答案 3 :(得分:2)

听起来问题可能是子进程使用缓冲输出 - 如果创建了相对少量的输出,则可以缓冲它直到子进程退出。可以找到一些背景here

答案 4 :(得分:1)

我也遇到过这个问题。出现此问题的原因是您也尝试读取stderr。如果没有错误,那么尝试从stderr读取将阻止。

在Windows上,没有简单的方法来轮询()文件描述符(只有Winsock套接字)。

因此,解决方案不是尝试从stderr读取。

答案 5 :(得分:1)

将pexpect [http://www.noah.org/wiki/Pexpect]与非阻塞读取线一起使用可以解决此问题。它源于管道被缓冲的事实,因此你的应用程序的输出被管道缓冲,因此在缓冲区填充或进程死亡之前你无法获得该输出。

答案 6 :(得分:1)

这似乎是一个众所周知的Python限制,请参阅 PEP 3145以及其他人。

答案 7 :(得分:1)

一次阅读一个字符:http://blog.thelinuxkid.com/2013/06/get-python-subprocess-output-without.html

import contextlib
import subprocess

# Unix, Windows and old Macintosh end-of-line
newlines = ['\n', '\r\n', '\r']
def unbuffered(proc, stream='stdout'):
    stream = getattr(proc, stream)
    with contextlib.closing(stream):
        while True:
            out = []
            last = stream.read(1)
            # Don't loop forever
            if last == '' and proc.poll() is not None:
                break
            while last not in newlines:
                # Don't loop forever
                if last == '' and proc.poll() is not None:
                    break
                out.append(last)
                last = stream.read(1)
            out = ''.join(out)
            yield out

def example():
    cmd = ['ls', '-l', '/']
    proc = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.STDOUT,
        # Make all end-of-lines '\n'
        universal_newlines=True,
    )
    for line in unbuffered(proc):
        print line

example()

答案 8 :(得分:1)

使用subprocess.Popen,我可以运行我的一个C#项目的.exe并将输出重定向到我的Python文件。我现在能够print()将所有信息输出到C#控制台(使用Console.WriteLine())到Python控制台。

Python代码:

from subprocess import Popen, PIPE, STDOUT

p = Popen('ConsoleDataImporter.exe', stdout = PIPE, stderr = STDOUT, shell = True)

while True:
    line = p.stdout.readline()
    print(line)
    if not line:
        break

这将在我的.NET项目创建时逐行获取控制台输出,并在项目终止时突破封闭的while循环。我想这也适用于两个python文件。

答案 9 :(得分:0)

我已经使用了pexpect模块,它似乎工作正常。 http://sourceforge.net/projects/pexpect/