[python] 하위 프로세스에서 실시간으로 stdout 포착

subprocess.Popen()Windows에서 rsync.exe 를 만들고 Python에서 stdout을 인쇄하고 싶습니다 .

내 코드는 작동하지만 파일 전송이 완료 될 때까지 진행 상황을 파악하지 못합니다! 각 파일의 진행 상황을 실시간으로 인쇄하고 싶습니다.

IO를 처리하는 것이 더 나을 것이라고 들었으므로 이제 Python 3.1을 사용하십시오.

import subprocess, time, os, sys

cmd = "rsync.exe -vaz -P source/ dest/"
p, line = True, 'start'


p = subprocess.Popen(cmd,
                     shell=True,
                     bufsize=64,
                     stdin=subprocess.PIPE,
                     stderr=subprocess.PIPE,
                     stdout=subprocess.PIPE)

for line in p.stdout:
    print(">>> " + str(line.rstrip()))
    p.stdout.flush()



답변

일부에 대한 엄지 손가락의 규칙 subprocess.

  • 사용 하지 마십시오shell=True . 프로그램을 호출하기 위해 불필요하게 추가 쉘 프로세스를 호출합니다.
  • 프로세스를 호출 할 때 인수는 목록으로 전달됩니다. sys.argv파이썬에서는 목록이고 argvC에서도 마찬가지입니다. 따라서 문자열이 아닌 하위 프로세스를 호출 하기 위해 목록 을 전달 Popen합니다.
  • 리디렉션하지 마십시오 stderrA와 PIPE당신이 그것을 읽을하지 않을 때.
  • 글을 쓰지 않을 stdin때 리디렉션하지 마십시오 .

예:

import subprocess, time, os, sys
cmd = ["rsync.exe", "-vaz", "-P", "source/" ,"dest/"]

p = subprocess.Popen(cmd,
                     stdout=subprocess.PIPE,
                     stderr=subprocess.STDOUT)

for line in iter(p.stdout.readline, b''):
    print(">>> " + line.rstrip())

즉, rsync가 터미널 대신 파이프에 연결되어 있음을 감지하면 출력을 버퍼링 할 가능성이 있습니다. 이것이 기본 동작입니다. 파이프에 연결될 때 프로그램은 실시간 결과를 위해 명시 적으로 stdout을 플러시해야합니다. 그렇지 않으면 표준 C 라이브러리가 버퍼링됩니다.

이를 테스트하려면 대신 다음을 실행하십시오.

cmd = [sys.executable, 'test_out.py']

test_out.py다음 내용 으로 파일을 만듭니다 .

import sys
import time
print ("Hello")
sys.stdout.flush()
time.sleep(10)
print ("World")

해당 하위 프로세스를 실행하면 “Hello”가 표시되고 “World”를 제공하기 전에 10 초 동안 기다려야합니다. 즉, 위의하지와 파이썬 코드를 발생하는 경우 rsync, 그 수단 rsync은 운이 그래서 자체가 출력을 버퍼링한다.

해결책은 pty같은 것을 사용하여에 직접 연결 하는 것 pexpect입니다.


답변

나는 이것이 오래된 주제라는 것을 알고 있지만 지금 해결책이 있습니다. –outbuf = L 옵션을 사용하여 rsync를 호출합니다. 예:

cmd=['rsync', '-arzv','--backup','--outbuf=L','source/','dest']
p = subprocess.Popen(cmd,
                     stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, b''):
    print '>>> {}'.format(line.rstrip())


답변

Linux에서는 버퍼링을 제거하는 것과 동일한 문제가있었습니다. 마지막으로 “stdbuf -o0″(또는 예상에서 버퍼링 해제)을 사용하여 PIPE 버퍼링을 제거했습니다.

proc = Popen(['stdbuf', '-o0'] + cmd, stdout=PIPE, stderr=PIPE)
stdout = proc.stdout

그런 다음 stdout에서 select.select를 사용할 수 있습니다.

참조 /unix/25372/


답변

사용 사례에 따라 하위 프로세스 자체에서 버퍼링을 비활성화 할 수도 있습니다.

하위 프로세스가 Python 프로세스 인 경우 호출 전에 다음을 수행 할 수 있습니다.

os.environ["PYTHONUNBUFFERED"] = "1"

또는 env인수에 이것을 전달하십시오 Popen.

그렇지 않고 Linux / Unix를 사용하는 경우 stdbuf도구를 사용할 수 있습니다 . 예 :

cmd = ["stdbuf", "-oL"] + cmd

또는 기타 옵션 에 대해서는 여기 를 참조하십시오 stdbuf.


답변

for line in p.stdout:
  ...

다음 줄 바꿈까지 항상 차단됩니다.

“실시간”동작의 경우 다음과 같이해야합니다.

while True:
  inchar = p.stdout.read(1)
  if inchar: #neither empty string nor None
    print(str(inchar), end='') #or end=None to flush immediately
  else:
    print('') #flush for implicit line-buffering
    break

while 루프는 자식 프로세스가 표준 출력을 닫거나 종료 할 때 남겨집니다.
read()/read(-1)자식 프로세스가 표준 출력을 닫거나 종료 될 때까지 차단됩니다.


답변

당신의 문제는 :

for line in p.stdout:
    print(">>> " + str(line.rstrip()))
    p.stdout.flush()

반복자 자체에는 추가 버퍼링이 있습니다.

다음과 같이 시도하십시오.

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


답변

stdout으로 버퍼링되지 않은 상태로 파이프에 인쇄 할 수 없습니다 (표준 출력으로 인쇄하는 프로그램을 다시 작성할 수없는 경우), 그래서 여기 내 해결책이 있습니다 :

stdout을 버퍼링되지 않은 sterr로 리디렉션합니다. '<cmd> 1>&2'해야합니다. 다음과 같이 프로세스를 엽니 다. myproc = subprocess.Popen('<cmd> 1>&2', stderr=subprocess.PIPE)
stdout 또는 stderr과 구별 할 수 없지만 모든 출력이 즉시 표시됩니다.

이것이이 문제를 해결하는 데 도움이되기를 바랍니다.