web-dev-qa-db-ja.com

subprocess.communicate()からストリーミング入力を読み取ります

Pythonのsubprocess.communicate()を使用して、約1分間実行されるプロセスから標準出力を読み取ります。

そのプロセスのstdoutの各行をストリーミング形式で印刷して、生成された出力を見ることができますが、続行する前にプロセスの終了をブロックするにはどうすればよいですか?

subprocess.communicate()は、すべての出力を一度に与えるように見えます。

注意してください、私は J.F。Sebastianの方法(下記) の方が良いと思います。


以下に簡単な例を示します(エラーのチェックなし)。

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

lsの終了が速すぎると、すべてのデータを読み取る前にwhileループが終了する場合があります。

この方法で、標準出力で残りをキャッチできます。

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

サブプロセスがstdoutバッファーをフラッシュするとすぐに、サブプロセスの出力を1行ずつ取得するには:

_#!/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が非対話型モードで行バッファリングの代わりにブロックバッファリングを使用する場合(子のバッファがいっぱいになるか、子によって明示的にフラッシュされるまで出力が遅延する場合)、 pexpectptyモジュール または unbufferstdbufscriptユーティリティQ:パイプ(popen())を使用しないのはなぜですか? を参照してください


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) encodingを使用してデコードされます) )。

136
jfs

ストリーミング形式でプロセスから出力を収集する最も簡単な方法は次のとおりです。

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()が望ましい場合があります-渡される数は任意ですが、それを除外すると、パイプ出力全体を一度に読み取ることになります。

5
D Coetzee

ノンブロッキングアプローチが必要な場合は、process.communicate()を使用しないでください。 subprocess.Popen()引数stdoutPIPEに設定すると、process.stdoutから読み取り、process.poll()を使用してプロセスがまだ実行されているかどうかを確認できます。

3

単にリアルタイムで出力を渡そうとしている場合、これよりも簡単にすることは困難です。

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()のドキュメント を参照してください。

出力を処理する必要がある場合は、必ずループしてください。ただし、そうしない場合は、単純にしてください。

Edit:JF Sebastian は、stdoutおよびstderrパラメーターのデフォルトがsys.stdoutおよびsysに渡されることの両方を指摘しています。 stderr、およびsys.stdoutとsys.stderrが置き換えられた場合、これは失敗します(たとえば、テストで出力をキャプチャするため)。

2
Nate
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'))
1
Petr J