[python] 프로세스가 실행되는 동안 지속적으로 하위 프로세스 출력을 인쇄

파이썬 스크립트에서 프로그램을 시작하려면 다음 방법을 사용하고 있습니다.

def execute(command):
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    output = process.communicate()[0]
    exitCode = process.returncode

    if (exitCode == 0):
        return output
    else:
        raise ProcessException(command, exitCode, output)

따라서와 같은 프로세스를 시작 Process.execute("mvn clean install")하면 프로세스가 완료 될 때까지 프로그램이 대기하고 프로그램의 전체 출력을 얻습니다. 완료하는 데 시간이 걸리는 프로세스를 실행하는 경우 짜증이납니다.

루프 또는 무언가가 끝나기 전에 프로세스 출력을 폴링하여 프로그램이 한 줄씩 프로세스 출력을 작성할 수 있습니까?

** [편집] 죄송합니다.이 질문을 게시하기 전에 검색이 잘되지 않았습니다. 스레딩이 실제로 핵심입니다. 방법을 보여주는 예제를 여기에서 찾았습니다 : **
Python Subprocess. 스레드에서 열기



답변

명령이 출력하는 즉시 iter 를 사용 하여 행을 처리 할 수 있습니다 lines = iter(fd.readline, ""). 다음은 일반적인 사용 사례를 보여주는 전체 예입니다 (도움을 주신 @jfs 덕분에).

from __future__ import print_function # Only Python 2.x
import subprocess

def execute(cmd):
    popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, universal_newlines=True)
    for stdout_line in iter(popen.stdout.readline, ""):
        yield stdout_line
    popen.stdout.close()
    return_code = popen.wait()
    if return_code:
        raise subprocess.CalledProcessError(return_code, cmd)

# Example
for path in execute(["locate", "a"]):
    print(path, end="")


답변

좋아, 나는이 질문에서 조각을 사용하여 (어떤 제안 스레드를 사용하는 이유는 더 나은 감사합니다 것) 스레드없이 그것을 해결하기 위해 관리 가 실행되는 동안 서브 프로세스의 표준 출력을 가로 채기를

def execute(command):
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    # Poll process for new output until finished
    while True:
        nextline = process.stdout.readline()
        if nextline == '' and process.poll() is not None:
            break
        sys.stdout.write(nextline)
        sys.stdout.flush()

    output = process.communicate()[0]
    exitCode = process.returncode

    if (exitCode == 0):
        return output
    else:
        raise ProcessException(command, exitCode, output)


답변

파이썬 3에서 stdout 버퍼가 플러시되는 즉시 서브 프로세스의 출력을 라인별로 인쇄하려면 :

from subprocess import Popen, PIPE, CalledProcessError

with Popen(cmd, stdout=PIPE, bufsize=1, universal_newlines=True) as p:
    for line in p.stdout:
        print(line, end='') # process line here

if p.returncode != 0:
    raise CalledProcessError(p.returncode, p.args)

주의 : 필요하지 않습니다 p.poll(). eof에 도달하면 루프가 종료됩니다. 그리고 당신은 필요하지 않습니다iter(p.stdout.readline, '') – 미리 읽기 버그는 파이썬 3에서 해결되었습니다.

Python : subprocess.communicate ()에서 스트리밍 입력 읽기를 참조하십시오 .


답변

실제로 출력 을 인쇄 하고 싶을 때 이렇게하는 간단한 방법이 있습니다 .

import subprocess
import sys

def execute(command):
    subprocess.check_call(command, stdout=sys.stdout, stderr=subprocess.STDOUT)

여기서 우리는 단순히 하위 프로세스를 자체 stdout을 가리키고 기존 성공 또는 예외 API를 사용합니다.


답변

@tokland

코드를 시도하고 3.4로 수정했으며 Windows dir.cmd는 cmd-file로 저장된 간단한 dir 명령입니다.

import subprocess
c = "dir.cmd"

def execute(command):
    popen = subprocess.Popen(command, stdout=subprocess.PIPE,bufsize=1)
    lines_iterator = iter(popen.stdout.readline, b"")
    while popen.poll() is None:
        for line in lines_iterator:
            nline = line.rstrip()
            print(nline.decode("latin"), end = "\r\n",flush =True) # yield line

execute(c)


답변

누군가가 두에서 읽고 싶은 경우 stdoutstderr스레드를 사용하여 동시에, 이것이 내가 생각 해낸 것입니다 :

import threading
import subprocess
import Queue

class AsyncLineReader(threading.Thread):
    def __init__(self, fd, outputQueue):
        threading.Thread.__init__(self)

        assert isinstance(outputQueue, Queue.Queue)
        assert callable(fd.readline)

        self.fd = fd
        self.outputQueue = outputQueue

    def run(self):
        map(self.outputQueue.put, iter(self.fd.readline, ''))

    def eof(self):
        return not self.is_alive() and self.outputQueue.empty()

    @classmethod
    def getForFd(cls, fd, start=True):
        queue = Queue.Queue()
        reader = cls(fd, queue)

        if start:
            reader.start()

        return reader, queue


process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdoutReader, stdoutQueue) = AsyncLineReader.getForFd(process.stdout)
(stderrReader, stderrQueue) = AsyncLineReader.getForFd(process.stderr)

# Keep checking queues until there is no more output.
while not stdoutReader.eof() or not stderrReader.eof():
   # Process all available lines from the stdout Queue.
   while not stdoutQueue.empty():
       line = stdoutQueue.get()
       print 'Received stdout: ' + repr(line)

       # Do stuff with stdout line.

   # Process all available lines from the stderr Queue.
   while not stderrQueue.empty():
       line = stderrQueue.get()
       print 'Received stderr: ' + repr(line)

       # Do stuff with stderr line.

   # Sleep for a short time to avoid excessive CPU use while waiting for data.
   sleep(0.05)

print "Waiting for async readers to finish..."
stdoutReader.join()
stderrReader.join()

# Close subprocess' file descriptors.
process.stdout.close()
process.stderr.close()

print "Waiting for process to exit..."
returnCode = process.wait()

if returnCode != 0:
   raise subprocess.CalledProcessError(returnCode, command)

나는이 질문에 비슷한 것을하려고 노력 하면서이 사실을 공유하고 싶었지만 대답으로 내 문제를 해결하지 못했습니다. 잘만되면 그것은 누군가를 돕는다!

내 유스 케이스에서 외부 프로세스가 우리 프로세스를 종료 Popen()시킵니다.


답변

파이썬 스크립트에서 stdout을 얻기 위해이 질문에 대한 답변을 시도하는 사람은 Python이 stdout을 버퍼링하므로 stdout을 보는 데 시간이 걸릴 수 있습니다.

대상 스크립트에서 각 stdout 쓰기 후에 다음을 추가하여이를 수정할 수 있습니다.

sys.stdout.flush()