하위 프로세스를 사용하여 실시간 출력 얻기
작업에 대한 진행률 표시기를 표시하는 명령 줄 프로그램 (svnadmin verify)에 대한 래퍼 스크립트를 작성하려고합니다. 이렇게하면 랩핑 된 프로그램의 출력 라인이 출력되는 즉시 볼 수 있어야합니다.
나는 subprocess.Popen
, use을 사용 하여 프로그램을 실행 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()
당신은 이것을 시도 할 수 있습니다 :
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을 사용하면 입력 메시지가 인쇄되지 않는 경우가 있습니다. 인라인 입력이 필요한 명령으로 시도해보십시오.
서브 프로세스 출력을 스트림으로 직접 지정할 수 있습니다. 단순화 된 예 :
subprocess.run(['ls'], stderr=sys.stderr, stdout=sys.stdout)
스트리밍 서브 프로세스의 표준 입력과 표준 출력과 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())
나는 다시 같은 문제에 부딪쳤다. 내 솔루션은 read
메서드에 대한 반복을 제거하는 것이 었습니다. 하위 프로세스가 실행을 마치지 않아도 즉시 반환됩니다.
실시간 출력 문제가 해결되었습니다 .C 프로그램에서 실시간 출력을 캡처하는 동안 Python에서도 비슷한 문제가 발생했습니다. " 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
도움이 되길 바랍니다.
~ 사이 람
유스 케이스에 따라 서브 프로세스 자체에서 버퍼링을 사용하지 않을 수도 있습니다.
하위 프로세스가 Python 프로세스 인 경우 호출 전에이를 수행 할 수 있습니다.
os.environ["PYTHONUNBUFFERED"] = "1"
또는에 env
인수로 이것을 전달하십시오 Popen
.
그렇지 않으면 Linux / Unix에있는 경우 stdbuf
도구를 사용할 수 있습니다 . 예 :
cmd = ["stdbuf", "-oL"] + cmd
참조 여기 에 대한 stdbuf
다른 옵션을 제공합니다.
( 동일한 답변 은 여기 를 참조하십시오 .)
이 솔루션을 사용하여 하위 프로세스에서 실시간 출력을 얻었습니다. 이 루프는 프로세스가 완료 되 자마자 break 문이나 무한 루프가 필요하지 않은 즉시 중지됩니다.
sub_process = subprocess.Popen(my_command, close_fds=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while sub_process.poll() is None:
out = sub_process.stdout.read(1)
sys.stdout.write(out)
sys.stdout.flush()
서브 프로세스의 출력에서 각 바이트에 대해 반복자를 사용할 수 있습니다. 서브 프로세스에서 인라인 업데이트 ( '\ r'로 끝나는 라인이 이전 출력 라인을 덮어 씁니다)를 허용합니다.
from subprocess import PIPE, Popen
command = ["my_command", "-my_arg"]
# Open pipe to subprocess
subprocess = Popen(command, stdout=PIPE, stderr=PIPE)
# read each byte of subprocess
while subprocess.poll() is None:
for c in iter(lambda: subprocess.stdout.read(1) if subprocess.poll() is None else {}, b''):
c = c.decode('ascii')
sys.stdout.write(c)
sys.stdout.flush()
if subprocess.returncode != 0:
raise Exception("The subprocess did not terminate correctly.")
비 차단 readlines와 함께 pexpect [ http://www.noah.org/wiki/Pexpect ]를 사용하면 이 문제가 해결됩니다. 파이프가 버퍼링되어 파이프에 의해 앱의 출력이 버퍼링되기 때문에 버퍼가 채워지거나 프로세스가 종료 될 때까지 해당 출력에 도달 할 수 없습니다.
Found this "plug-and-play" function here. Worked like a charm!
import subprocess
def myrun(cmd):
"""from http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html
"""
p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout = []
while True:
line = p.stdout.readline()
stdout.append(line)
print line,
if line == '' and p.poll() != None:
break
return ''.join(stdout)
Complete solution:
import contextlib
import subprocess
# Unix, Windows and old Macintosh end-of-line
newlines = ['\n', '\r\n', '\r']
def unbuffered(proc, stream='stdout'):
stream = getattr(proc, stream)
with contextlib.closing(stream):
while True:
out = []
last = stream.read(1)
# Don't loop forever
if last == '' and proc.poll() is not None:
break
while last not in newlines:
# Don't loop forever
if last == '' and proc.poll() is not None:
break
out.append(last)
last = stream.read(1)
out = ''.join(out)
yield out
def example():
cmd = ['ls', '-l', '/']
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
# Make all end-of-lines '\n'
universal_newlines=True,
)
for line in unbuffered(proc):
print line
example()
This is the basic skeleton that I always use for this. It makes it easy to implement timeouts and is able to deal with inevitable hanging processes.
import subprocess
import threading
import Queue
def t_read_stdout(process, queue):
"""Read from stdout"""
for output in iter(process.stdout.readline, b''):
queue.put(output)
return
process = subprocess.Popen(['dir'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
cwd='C:\\',
shell=True)
queue = Queue.Queue()
t_stdout = threading.Thread(target=t_read_stdout, args=(process, queue))
t_stdout.daemon = True
t_stdout.start()
while process.poll() is None or not queue.empty():
try:
output = queue.get(timeout=.5)
except Queue.Empty:
continue
if not output:
continue
print(output),
t_stdout.join()
(This solution has been tested with Python 2.7.15)
You just need to sys.stdout.flush() after each line read/write:
while proc.poll() is None:
line = proc.stdout.readline()
sys.stdout.write(line)
# or print(line.strip()), you still need to force the flush.
sys.stdout.flush()
In Python 3.x the process might hang because the output is a byte array instead of a string. Make sure you decode it into a string.
Starting from Python 3.6 you can do it using the parameter encoding
in Popen Constructor. The complete example:
process = subprocess.Popen(
'my_command',
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True,
encoding='utf-8',
errors='replace'
)
while True:
realtime_output = process.stdout.readline()
if realtime_output == '' and process.poll() is not None:
break
if realtime_output:
print(realtime_output.strip(), flush=True)
Note that this code redirects stderr
to stdout
and handles output errors.
참고URL : https://stackoverflow.com/questions/803265/getting-realtime-output-using-subprocess
'development' 카테고리의 다른 글
C #에서 filename이 * 가능한지 * 유효한지 확인하십시오 (존재하지 않음) (0) | 2020.07.12 |
---|---|
부스트 경로 유형을 문자열로 변환하는 방법은 무엇입니까? (0) | 2020.07.12 |
NumPy 배열의 모든 셀에서 함수를 효율적으로 평가 (0) | 2020.07.12 |
Java에서 어떤 동시 큐 구현을 사용해야합니까? (0) | 2020.07.12 |
뷰 모델 (C #)에서 WPF의 텍스트 상자에 포커스 설정 (0) | 2020.07.11 |