47

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

  1. 创建一个可能需要很长时间才能执行的新进程。
  2. 捕获stdout(或stderr,或可能两者,一起或单独)
  3. 处理来自子进程的数据可能会在接收到的每一行上触发事件(在 wxPython 中说),或者现在只是将它们打印出来。

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

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

有没有办法解决这个问题,并且在多个平台上运行良好?

4

10 回答 10

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
于 2009-05-17T19:16:53.203 回答
7

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

您可以尝试stdout从子进程中刷新,或使用标准错误,或在非缓冲模式下更改标准输出。

于 2009-05-17T15:51:49.717 回答
2

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

于 2009-05-17T15:53:38.610 回答
2

这对我有用:

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()as in python替换它0(成功终止的返回码)也被考虑False

于 2009-12-22T10:06:00.163 回答
1

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

于 2010-05-18T15:42:43.557 回答
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()
于 2014-06-21T23:02:35.527 回答
1

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

于 2013-03-29T07:39:02.090 回答
1

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

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

所以一个解决方案是不要尝试从标准错误中读取。

于 2010-02-24T17:37:13.897 回答
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 文件。

于 2016-08-04T19:05:36.527 回答
0

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

于 2009-09-21T18:51:59.427 回答