如何在Python中从子进程PIPE获取数据,而子进程正在运行?

3
我在Windows系统上有一个程序,它会调用许多子进程,并在GUI中显示结果。我使用PyQt来创建GUI,使用subprocess模块运行程序。
我有以下WorkerThread函数,为每个shell命令生成一个子线程来读取进程的stdout并打印结果(稍后将其连接到GUI)。
这一切都有效。但是,proc.stdout.read(1)直到子进程完成之后才会返回。这是一个大问题,因为其中一些子过程可能需要15-20分钟才能运行,而我需要实时显示结果。
我需要做什么才能在子进程运行时使管道正常工作?
class WorkerThread(QtCore.QThread):
    def run(self):
        def sh(cmd, cwd = None):
            proc = subprocess.Popen(cmd,
                                    shell = True,
                                    stdout = subprocess.PIPE,
                                    stderr = subprocess.STDOUT,
                                    stdin = subprocess.PIPE,
                                    cwd = cwd,
                                    env = os.environ)

            proc.stdin.close()

            class ReadStdOutThread(QtCore.QThread):
                def run(_self):
                    s = ''
                    while True:
                        if self.request_exit: return

                        b = proc.stdout.read(1)
                        if b == '\n':
                            print s
                            s = ''
                            continue

                        if b:
                            s += b
                            continue

                        if s: print s
                        return

            thread = ReadStdOutThread()
            thread.start()

            retcode = proc.wait()
            if retcode:
                raise subprocess.CalledProcessError(retcode, cmd)

            return 0

值得一提的是:我使用QProcess重写了整个过程,但是我遇到了同样的问题。在底层进程返回之前,stdout接收不到任何数据,然后我才会一次性收到所有东西。

1个回答

1
如果您知道命令输出的行数,可以对进程的stdout PIPE进行轮询。下面是一个例子:
import select
import subprocess
import threading
import os

# Some time consuming command.
command = 'while [ 1 ]; do sleep 1; echo "Testing"; done'

# A worker thread, not as complex as yours, just to show my point.
class Worker(threading.Thread):

    def __init__(self):
        super(Worker, self).__init__()
        self.proc = subprocess.Popen(
            command, shell=True, 
            stdout=subprocess.PIPE, 
            stdin=subprocess.PIPE, stderr=subprocess.STDOUT
        )

    def run(self):
        self.proc.communicate()


    def get_proc(self):
        # The proc is needed for ask him for his
        # output file descriptor later.
        return self.proc


if __name__ == '__main__':
    w = Worker()
    w.start()

    proc = w.get_proc()

    pollin = select.poll()

    pollin.register(proc.stdout, select.POLLIN)


    while ( 1 ):
        events = pollin.poll()
        for fd, event in events:
             if event == select.POLLIN:
                 # This is the main issue of my idea,
                 # if you don't know the length of lines
                 # that process ouput, this is a problem.
                 # I put 7 since I know the word "Testing" have
                 # 7 characters.
                 print os.read(fd, 7)

也许这并不完全符合您的要求,但我认为它可以给您提供解决问题的很好的思路。
编辑:我认为我刚刚找到了您需要的在Python中从Python子进程流式传输stdout

我不知道输出会有多长。 - Chris B.
@ChrisB。我刚刚添加了一个链接到一个例子,这可能是你正在寻找的。 - Raydel Miranda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接