エグゼクティブサマリー(または「tl; dr」バージョン):が1つ以下の場合は簡単ですがsubprocess.PIPE、そうでない場合は難しいです。
どのようにsubprocess.Popen機能するかについて少し説明するときかもしれません。
(注意:これはPython 2.xの場合ですが、3.xも同様です。Windowsバリアントについては非常にあいまいです。POSIXについてはもっとよく理解しています。)
このPopen関数は、ゼロから3つのI / Oストリームを多少同時に処理する必要があります。これらは表記されstdin、stdoutそして、stderrいつものように。
以下を提供できます。
None、ストリームをリダイレクトしないことを示します。代わりに通常どおりこれらを継承します。少なくともPOSIXシステムでは、これはPythonのを使用することを意味するのではなくsys.stdout、Pythonの実際の stdout のみを使用することに注意してください。最後にデモをご覧ください。
int値。これは(少なくともPOSIXでは)「生の」ファイル記述子です。(傍注:PIPEおよびSTDOUTは実際にはint内部で使用されていますが、「不可能」な記述子であり、-1および-2です。)
- ストリーム—実際には、
filenoメソッドを持つオブジェクト。 Popenを使用してstream.fileno()、そのストリームの記述子を検索し、int値の場合と同様に処理します。
subprocess.PIPE、Pythonがパイプを作成する必要があることを示します。
subprocess.STDOUT(stderrのみ):と同じ記述子を使用するようにPythonに指示しますstdout。これは、に(非None)値を指定した場合にのみ意味がstdoutあり、それでも、を設定した場合にのみ必要ですstdout=subprocess.PIPE。(そうしないと、あなたはちょうどあなたがのために提供される同じ引数を提供することができstdout、例えば、Popen(..., stdout=stream, stderr=stream)。)
最も簡単なケース(パイプなし)
何もリダイレクトしない場合(3つすべてをデフォルトNone値のままにするかNone、明示的に指定する)、Pipeそれは非常に簡単です。サブプロセスを分離して実行させるだけです。あなたが非にリダイレクトする場合は、PIPE-an intまたはストリームだfileno()OSは、すべての作業を行うよう-itは、簡単にはまだです。Pythonはサブプロセスをスピンオフし、そのstdin、stdout、および/またはstderrを提供されたファイル記述子に接続するだけです。
まだ簡単なケース:1つのパイプ
ストリームを1つだけリダイレクトしPipeても、非常に簡単です。一度に1つのストリームを選択して見てみましょう。
あなたには、いくつかを供給するとしますstdinが、聞かせてstdoutとstderr非リダイレクト行く、またはファイル記述子にアクセスしてください。親プロセスとして、Pythonプログラムwrite()はパイプを使用してデータを送信するために使用する必要があるだけです。これは自分で行うことができます。例:
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
proc.stdin.write('here, have some data\n') # etc
または、あなたはに標準入力データを渡すことができproc.communicate()、その後んれ、stdin.write上に示しました。出力は返されないので、communicate()他に1つだけ実際のジョブがあります。パイプも閉じます。(呼び出さない場合は、呼び出してパイプを閉じるproc.communicate()必要がありますproc.stdin.close()。これにより、サブプロセスがデータがなくなったことを認識できます。)
キャプチャしたいstdoutがstdin、stderr一人にしたいとします。繰り返しますが、簡単ですproc.stdout.read()。出力がなくなるまで(または同等のものを)呼び出すだけです。以来proc.stdout()、通常のPython Iである/ Oあなたはのように、それにすべての通常の構文を使用することができますストリーム:
for line in proc.stdout:
または、もう一度、あなたが使用することができproc.communicate()、単純にしている、read()あなたのため。
のみをキャプチャする場合はstderr、と同じように機能しstdoutます。
物事が困難になる前に、もう1つのトリックがあります。あなたがキャプチャしたいとしstdout、また、キャプチャstderrが、標準出力と同じパイプに:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
この場合、subprocess「カンニング」!まあ、これを行う必要があるので、実際には不正行為ではありません。親プロセス(Python)にフィードバックする(単一の)パイプ記述子にstdoutとstderrの両方を指定して、サブプロセスを開始します。親側では、出力を読み取るためのパイプ記述子が1つだけあります。すべての「stderr」出力はに表示されproc.stdout、を呼び出すproc.communicate()と、stderrの結果(タプルの2番目の値)はNone文字列ではなくになります。
ハードケース:2つ以上のパイプ
少なくとも2つのパイプを使用したいときに問題が発生します。実際、subprocessコード自体には次のビットがあります。
def communicate(self, input=None):
...
# Optimization: If we are only using one pipe, or no pipe at
# all, using select() or threads is unnecessary.
if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
しかし、悲しいかな、ここでは少なくとも2つ、場合によっては3つの異なるパイプを作成したので、count(None)戻り値は1または0のどちらかになります。
Windowsでは、この用途threading.Threadのための蓄積の結果とself.stdoutしてself.stderr、とは、親スレッドが提供する持っているself.stdin(そして近いとパイプ)入力データを。
POSIXでは、poll利用可能な場合はこれを使用し、そうでない場合はselect、出力を蓄積してstdin入力を配信します。これはすべて、(単一の)親プロセス/スレッドで実行されます。
デッドロックを回避するには、スレッドまたはポーリング/選択が必要です。たとえば、3つのストリームすべてを3つの別々のパイプにリダイレクトしたとします。さらに、書き込みプロセスが一時停止し、読み取りプロセスがパイプをもう片方から「一掃」するのを待機する前に、パイプに詰め込むことができるデータ量に小さな制限があるとします。説明のために、その小さな制限を1バイトに設定してみましょう。(実際には、制限が1バイトよりもはるかに大きいことを除いて、これはどのように機能するかです。)
親(Python)プロセスがいくつかのバイトを書き込もうとすると、たとえば'go\n'toのproc.stdin場合、最初のバイトが入り、次に2番目のプロセスがPythonプロセスを中断し、サブプロセスが最初のバイトを読み取るのを待って、パイプを空にします。
一方、サブプロセスが「Hello!Do n't Panic!」というフレンドリーなメッセージを印刷することにしたとします。挨拶。Hその標準出力パイプになりますが、eその親がそれを読むのを待って、中断することの原因となるH標準出力パイプを空にし、。
これでスタックしました。Pythonプロセスはスリープ状態で、「go」と言うのを完了するのを待っています。また、サブプロセスもスリープ状態で、「Hello!Do n't Panic!」と言うのを完了するのを待っています。
subprocess.Popenコードはスレッド-または選択/世論調査で、この問題を回避することができます。バイトがパイプを通過できるとき、それらは行きます。できない場合は、スレッド全体(プロセス全体ではなく)だけがスリープ状態になります。または、select / pollの場合、Pythonプロセスは「書き込み可能」または「データが利用可能」になるまで同時に待機し、プロセスの標準入力に書き込みます空きがある場合にのみ、データの準備ができている場合にのみstdoutやstderrを読み取ります。proc.communicate()コード(実際には_communicateすべての標準入力データ(もしあれば)一度ヘアリーケースが処理される)戻って送信された、すべてのstdoutおよび/またはstderrのデータが蓄積されています。
(リダイレクトに関係なく)両方stdoutとstderr2つの異なるパイプで読み取る場合はstdin、デッドロックも回避する必要があります。ここでのデッドロックシナリオは異なりstderrます。stdoutつまり、からデータをプルしている間にサブプロセスが何かに長い書き込みを行った場合、またはその逆の場合に発生しますが、それでもまだあります。
デモ
リダイレクトされずに、Pythonがsubprocess、ではなく、基になるstdoutに書き込むことを示すことを約束しましたsys.stdout。だから、ここにいくつかのコードがあります:
from cStringIO import StringIO
import os
import subprocess
import sys
def show1():
print 'start show1'
save = sys.stdout
sys.stdout = StringIO()
print 'sys.stdout being buffered'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
in_stdout = sys.stdout.getvalue()
sys.stdout = save
print 'in buffer:', in_stdout
def show2():
print 'start show2'
save = sys.stdout
sys.stdout = open(os.devnull, 'w')
print 'after redirect sys.stdout'
proc = subprocess.Popen(['echo', 'hello'])
proc.wait()
sys.stdout = save
show1()
show2()
実行すると:
$ python out.py
start show1
hello
in buffer: sys.stdout being buffered
start show2
hello
オブジェクトにはないstdout=sys.stdoutので、追加すると最初のルーチンは失敗することに注意してください。2番目は、にリダイレクトされているため、追加した場合は省略されます。StringIOfilenohellostdout=sys.stdoutsys.stdoutos.devnull
(Pythonのfile-descriptor-1 をリダイレクトすると、サブプロセスはそのリダイレクトに従います。open(os.devnull, 'w')呼び出しにより、fileno()2より大きいストリームが生成されます。)
Popen.pollように使用できます。