[python] 하위 프로세스를 사용하여 실시간 출력 얻기

작업에 대한 진행률 표시기가 표시되는 명령 줄 프로그램 (svnadmin verify)에 대한 래퍼 스크립트를 작성하려고합니다. 이렇게하면 랩핑 된 프로그램의 출력 라인이 출력되는 즉시 볼 수 있어야합니다.

나는을 사용하여 프로그램을 실행하고 사용 subprocess.Popen하고 stdout=PIPE각 줄을 읽은 다음 그에 따라 행동한다고 ​​생각했다. 그러나 다음 코드를 실행하면 출력이 어딘가에 버퍼링되어 1 ~ 332 줄, 333 ~ 439 (마지막 출력 줄)의 두 덩어리로 나타납니다.

from subprocess import Popen, PIPE, STDOUT

p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE,
        stderr = STDOUT, shell = True)
for line in p.stdout:
    print line.replace('\n', '')

하위 프로세스에 대한 문서를 약간 살펴본 후 bufsize매개 변수를로 찾았 Popen으므로 bufsize를 1 (각 행 버퍼) 및 0 (버퍼 없음)으로 설정하려고 시도했지만 값이 행 전달 방식을 변경하지 않는 것 같습니다.

이 시점에서 나는 빨대를 파악하기 시작했으며 다음과 같은 출력 루프를 작성했습니다.

while True:
    try:
        print p.stdout.next().replace('\n', '')
    except StopIteration:
        break

그러나 같은 결과를 얻었습니다.

하위 프로세스를 사용하여 실행 된 프로그램의 ‘실시간’프로그램 출력을 얻을 수 있습니까? 파이썬에서 순방향 호환 (아닌 exec*) 다른 옵션이 있습니까?



답변

나는 이것을 시도했고 어떤 이유로 코드가있는 동안

for line in p.stdout:
  ...

적극적으로 버퍼를 변형

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

하지 않습니다. : 분명히 이것은 알려진 버그 http://bugs.python.org/issue3907 (이 문제는 이제 2018 년 8 월 29로 “닫기”는)


답변

p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1)
for line in iter(p.stdout.readline, b''):
    print line,
p.stdout.close()
p.wait()


답변

서브 프로세스 출력을 스트림으로 직접 지정할 수 있습니다. 단순화 된 예 :

subprocess.run(['ls'], stderr=sys.stderr, stdout=sys.stdout)


답변

당신은 이것을 시도 할 수 있습니다 :

import subprocess
import sys

process = subprocess.Popen(
    cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)

while True:
    out = process.stdout.read(1)
    if out == '' and process.poll() != None:
        break
    if out != '':
        sys.stdout.write(out)
        sys.stdout.flush()

읽기 대신 readline을 사용하면 입력 메시지가 인쇄되지 않는 경우가 있습니다. 인라인 입력이 필요한 명령으로 시도해보십시오.


답변

스트리밍 서브 프로세스의 표준 입력과 표준 출력과 asyncio 파이썬에 의한 블로그 포스트 케빈 맥카시의 asyncio으로 작업을 수행하는 방법을 보여줍니다 :

import asyncio
from asyncio.subprocess import PIPE
from asyncio import create_subprocess_exec


async def _read_stream(stream, callback):
    while True:
        line = await stream.readline()
        if line:
            callback(line)
        else:
            break


async def run(command):
    process = await create_subprocess_exec(
        *command, stdout=PIPE, stderr=PIPE
    )

    await asyncio.wait(
        [
            _read_stream(
                process.stdout,
                lambda x: print(
                    "STDOUT: {}".format(x.decode("UTF8"))
                ),
            ),
            _read_stream(
                process.stderr,
                lambda x: print(
                    "STDERR: {}".format(x.decode("UTF8"))
                ),
            ),
        ]
    )

    await process.wait()


async def main():
    await run("docker build -t my-docker-image:latest .")


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())


답변

실시간 출력 문제 해결 : 파이썬에서 비슷한 문제가 발생하여 c 프로그램에서 실시간 출력을 캡처합니다. ” fflush (stdout) ;”를 추가했습니다 . 내 C 코드에서. 그것은 나를 위해 일했다. 다음은 코드 스니핑입니다.

《C 프로그램》

#include <stdio.h>
void main()
{
    int count = 1;
    while (1)
    {
        printf(" Count  %d\n", count++);
        fflush(stdout);
        sleep(1);
    }
}

<< 파이썬 프로그램 >>

#!/usr/bin/python

import os, sys
import subprocess


procExe = subprocess.Popen(".//count", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)

while procExe.poll() is None:
    line = procExe.stdout.readline()
    print("Print:" + line)

<< 출력 >> 인쇄 : 카운트 1 인쇄 : 카운트 2 인쇄 : 카운트 3

도움이 되길 바랍니다.

~ 사이 람


답변

나는 다시 같은 문제에 부딪쳤다. 내 솔루션은 read메서드에 대한 반복을 제거하는 것이 었습니다. 하위 프로세스가 실행을 마치지 않아도 즉시 반환됩니다.