web-dev-qa-db-ja.com

実行中にサブプロセスのstdoutをインターセプトする

これが私のサブプロセスの場合:

import time, sys
for i in range(200):
    sys.stdout.write( 'reading %i\n'%i )
    time.sleep(.02)

そして、これはサブプロセスの出力を制御および変更するスクリプトです。

import subprocess, time, sys

print 'starting'

proc = subprocess.Popen(
    'c:/test_apps/testcr.py',
    Shell=True,
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE  )

print 'process created'

while True:
    #next_line = proc.communicate()[0]
    next_line = proc.stdout.readline()
    if next_line == '' and proc.poll() != None:
        break
    sys.stdout.write(next_line)
    sys.stdout.flush()

print 'done'

プロセスの実行が完了するまでreadlinecommunicateが待機しているのはなぜですか?サブプロセスの標準出力をリアルタイムで渡す(および変更する)簡単な方法はありますか?

ところで、私は this を見てきましたが、ロギング機能は必要ありません(そして、その多くをわざわざ理解する必要はありません)。

私はWindowsXPを使用しています。

23
Paul

チャールズがすでに述べたように、問題はバッファリングです。 SNMPd用のいくつかのモジュールを作成するときに同様の問題が発生し、stdoutを自動フラッシュバージョンに置き換えることで問題を解決しました。

ActiveStateのいくつかの投稿に触発されて、次のコードを使用しました。

class FlushFile(object):
    """Write-only flushing wrapper for file-type objects."""
    def __init__(self, f):
        self.f = f
    def write(self, x):
        self.f.write(x)
        self.f.flush()

# Replace stdout with an automatically flushing version
sys.stdout = FlushFile(sys.__stdout__)
15
Kamil Kisiel

プロセス出力はバッファリングされます。より多くのUNIXオペレーティングシステム(またはCygwin)では、 pexpect モジュールが使用可能であり、バッファリング関連の問題を回避するために必要なすべての呪文を列挙します。ただし、これらのインカンテーションには、動作する pty module が必要です。これは、ネイティブ(非cygwin)win32 Pythonビルドでは使用できません。

サブプロセスを制御する例の場合、必要に応じてsys.stdout.flush()を呼び出すことができますが、任意のサブプロセスの場合、そのオプションは使用できません。

Pexpect FAQの 「パイプ(popen())を使用しないのはなぜですか?」 も参照してください。

8
Charles Duffy