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 솔루션 추가 :
- 쉘 프로세스의 종료 코드를 잡을 수 있습니다 (
with
구성 을 사용하는 동안 종료 코드를 가져올 수 없습니다 ). - 또한 실시간으로 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)