subprocess.communicate ()에서 스트리밍 입력 읽기 방식으로 인쇄하여 생성 된

subprocess.communicate()약 1 분 동안 실행되는 프로세스에서 표준 출력을 읽기 위해 Python을 사용 하고 있습니다.

해당 프로세스의 각 줄을 stdout스트리밍 방식으로 인쇄하여 생성 된 출력을 볼 수 있지만 계속하기 전에 프로세스가 종료되는 것을 차단하려면 어떻게해야합니까?

subprocess.communicate() 한 번에 모든 출력을 제공하는 것처럼 보입니다.



답변

내가 생각하는주의 사항 (아래) JF 세바스찬의 방법은 더 좋다.


다음은 오류를 확인하지 않는 간단한 예입니다.

import subprocess
proc = subprocess.Popen('ls',
                       shell=True,
                       stdout=subprocess.PIPE,
                       )
while proc.poll() is None:
    output = proc.stdout.readline()
    print output,

경우 ls모든 데이터를 읽은 전에 끝이 너무 빨리, 그 동안 루프가 종료 될 수 있습니다.

다음과 같이 stdout에서 나머지를 잡을 수 있습니다.

output = proc.communicate()[0]
print output,


답변

하위 프로세스가 stdout 버퍼를 플러시하자마자 하위 프로세스의 출력을 한 줄씩 가져 오려면 :

#!/usr/bin/env python2
from subprocess import Popen, PIPE

p = Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1)
with p.stdout:
    for line in iter(p.stdout.readline, b''):
        print line,
p.wait() # wait for the subprocess to exit

iter()Python 2의 미리 읽기 버그 를 해결 하기 위해 작성되는 즉시 행을 읽는 데 사용됩니다 .

하위 프로세스의 stdout이 비대화 형 모드에서 라인 버퍼링 대신 블록 버퍼링을 사용하는 경우 (이로 인해 자식의 버퍼가 가득 차거나 자식이 명시 적으로 플러시 할 때까지 출력이 지연됨) 다음을 사용하여 버퍼링되지 않은 출력을 강제로 시도 할 수 있습니다. pexpect, pty모듈 또는 unbuffer, stdbuf, script유틸리티 , 참조 왜 그냥 파이프 사용 (는 popen을 ()) : Q는?


다음은 Python 3 코드입니다.

#!/usr/bin/env python3
from subprocess import Popen, PIPE

with Popen(["cmd", "arg1"], stdout=PIPE, bufsize=1,
           universal_newlines=True) as p:
    for line in p.stdout:
        print(line, end='')

참고 : 하위 프로세스의 바이트 문자열을 그대로 출력하는 Python 2와 달리; Python 3은 텍스트 모드를 사용합니다 (cmd의 출력은 locale.getpreferredencoding(False)인코딩을 사용하여 디코딩 됨 ).


답변

스트리밍 방식으로 프로세스에서 출력을 수집하는 가장 간단한 방법은 다음과 같습니다.

import sys
from subprocess import *
proc = Popen('ls', shell=True, stdout=PIPE)
while True:
    data = proc.stdout.readline()   # Alternatively proc.stdout.read(1024)
    if len(data) == 0:
        break
    sys.stdout.write(data)   # sys.stdout.buffer.write(data) on Python 3.x

readline()또는 read()읽을 수있는 것도이 (가없는 경우 그렇지 않은 경우는 차단 – 기능은 프로세스가 종료 된 후, EOF에 빈 문자열을 반환해야합니다 readline(), 줄 바꿈을 포함하므로 빈 줄에, 그것은 반환 “\ n”). 이렇게하면 communicate()루프 후 어색한 최종 호출 이 필요하지 않습니다 .

read()이 매우 긴 파일에서는 최대 메모리 사용량을 줄이는 것이 바람직 할 수 있습니다. 전달 된 수는 임의적이지만이를 제외하면 전체 파이프 출력을 한 번에 읽는 것이 바람직하지 않을 수 있습니다.


답변

비 차단 방식을 원하면 process.communicate(). subprocess.Popen()인수 stdout를로 설정하면 PIPE에서 읽고 process.stdout프로세스가 여전히을 사용하여 실행되는지 확인할 수 있습니다 process.poll().


답변

단순히 실시간으로 출력을 전달하려는 경우 다음보다 더 간단하게하기가 어렵습니다.

import subprocess

# This will raise a CalledProcessError if the program return a nonzero code.
# You can use call() instead if you don't care about that case.
subprocess.check_call(['ls', '-l'])

subprocess.check_call ()에 대한 문서를 참조하십시오 .

출력을 처리해야하는 경우에는 루프를 실행하십시오. 그러나 그렇지 않다면 간단하게 유지하십시오.

편집 : JF Sebastian 은 stdout 및 stderr 매개 변수의 기본값이 sys.stdout 및 sys.stderr로 전달되고 sys.stdout 및 sys.stderr이 교체되면 실패한다고 지적합니다 (예 : 테스트).


답변

myCommand="ls -l"
cmd=myCommand.split()
# "universal newline support" This will cause to interpret \n, \r\n and \r     equally, each as a newline.
p = subprocess.Popen(cmd, stderr=subprocess.PIPE, universal_newlines=True)
while True:
    print(p.stderr.readline().rstrip('\r\n'))


답변

몇 가지 작은 변경 사항으로 다른 python3 솔루션 추가 :

  1. 쉘 프로세스의 종료 코드를 잡을 수 있습니다 ( with구성 을 사용하는 동안 종료 코드를 가져올 수 없습니다 ).
  2. 또한 실시간으로 stderr을 파이프합니다.
import subprocess
import sys
def subcall_stream(cmd, fail_on_error=True):
    # Run a shell command, streaming output to STDOUT in real time
    # Expects a list style command, e.g. `["docker", "pull", "ubuntu"]`
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True)
    for line in p.stdout:
        sys.stdout.write(line)
    p.wait()
    exit_code = p.returncode
    if exit_code != 0 and fail_on_error:
        raise RuntimeError(f"Shell command failed with exit code {exit_code}. Command: `{cmd}`")
    return(exit_code)