如何从 python (2.5) 中的 subprocess.Popen 获取“实时”信息

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/874815/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 21:02:08  来源:igfitidea点击:

How do I get 'real-time' information back from a subprocess.Popen in python (2.5)

pythonsubprocessstdoutpopen

提问by Ryan

I'd like to use the subprocess module in the following way:

我想通过以下方式使用 subprocess 模块:

  1. create a new process that potentially takes a long time to execute.
  2. capture stdout(or stderr, or potentially both, either together or separately)
  3. Process data from the subprocess as it comes in,perhaps firing events on every line received (in wxPython say) or simply printing them out for now.
  1. 创建一个可能需要很长时间才能执行的新进程。
  2. 捕获stdout(或stderr,或可能同时捕获或单独捕获)
  3. 处理来自子进程的数据可能会在收到的每一行上触发事件(在 wxPython 中),或者只是暂时将它们打印出来。

I've created processes with Popen, but if I use communicate() the data comes at me all at once, once the process has terminated.

我已经用 Popen 创建了进程,但是如果我使用communication(),一旦进程终止,数据就会立即传给我。

If I create a separate thread that does a blocking readline()of myprocess.stdout(using stdout = subprocess.PIPE) I don't get any lines with this method either, until the process terminates. (no matter what I set as bufsize)

如果我创建一个单独的线程,做了阻塞readline()myprocess.stdout(使用stdout = subprocess.PIPE)我不明白这种方法的任何行或者,直到进程终止。(无论我设置为 bufsize)

Is there a way to deal with this that isn't horrendous, and works well on multiple platforms?

有没有办法解决这个问题,既不可怕又可以在多个平台上运行良好?

回答by Ryan

Update with code that appears not to work (on windows anyway)

使用似乎不起作用的代码更新(无论如何在 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

回答by Douglas Leeder

stdout will be buffered - so you won't get anything till that buffer is filled, or the subprocess exits.

stdout 将被缓冲 - 因此在该缓冲区被填满或子进程退出之前您将不会得到任何东西。

You can try flushing stdoutfrom the sub-process, or using stderr, or changing stdout on non-buffered mode.

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

回答by Lance Richardson

It sounds like the issue might be the use of buffered output by the subprocess - if a relatively small amount of output is created, it could be buffered until the subprocess exits. Some background can be found here:

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

回答by exhuma

Here's what worked for me:

这是对我有用的:

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 )

In your case you could try to pass a reference to the sub-process to your Worker Thread, and do the polling inside the thread. I don't know how it will behave when two threads poll (and interact with) the same subprocess, but it may work.

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

Also note thate the while p.poll() is None:is intended as is. Do notreplace it with while not p.poll()as in python 0(the returncode for successful termination) is also considered False.

另请注意,这while p.poll() is None:是按原样设计的。千万不能将其替换为while not p.poll()在蟒蛇0(成功终止返回码)也被认为是False

回答by khcheng

I've been running into this problem as well. The problem occurs because you are trying to read stderr as well. If there are no errors, then trying to read from stderr would block.

我也一直遇到这个问题。出现问题是因为您也试图读取 stderr。如果没有错误,则尝试从 stderr 读取会阻塞。

On Windows, there is no easy way to poll() file descriptors (only Winsock sockets).

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

So a solution is not to try and read from stderr.

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

回答by Gabe

Using pexpect [http://www.noah.org/wiki/Pexpect]with non-blocking readlines will resolve this problem. It stems from the fact that pipes are buffered, and so your app's output is getting buffered by the pipe, therefore you can't get to that output until the buffer fills or the process dies.

使用 pexpect [ http://www.noah.org/wiki/Pexpect]和非阻塞 readlines 将解决这个问题。它源于管道被缓冲的事实,因此您的应用程序的输出被管道缓冲,因此在缓冲区填满或进程终止之前您无法获得该输出。

回答by MarcH

This seems to be a well-known Python limitation, see PEP 3145and maybe others.

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

回答by Andres Restrepo

Read one character at a time: http://blog.thelinuxkid.com/2013/06/get-python-subprocess-output-without.html

一次读取一个字符: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()

回答by Karan Narula

Using subprocess.Popen, I can run the .exe of one of my C# projects and redirect the output to my Python file. I am able now to print()all the information being output to the C# console (using Console.WriteLine()) to the Python console.

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

Python code:

蟒蛇代码:

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

This gets the console output of my .NET project line by line as it is created and breaks out of the enclosing while loop upon the project's termination. I'd imagine this would work for two python files as well.

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

回答by Karan Narula

I've used the pexpect module for this, it seems to work ok. http://sourceforge.net/projects/pexpect/

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