web-dev-qa-db-ja.com

サブプロセスから「リアルタイム」情報を取得するにはどうすればよいですか。Popeninpython(2.5)

サブプロセスモジュールを次のように使用したいと思います。

  1. 実行に時間がかかる可能性のある新しいプロセスを作成します。
  2. キャプチャstdout(またはstderr、あるいは両方、一緒にまたは別々に)
  3. サブプロセスからのデータを処理します入ってくるとおそらく(wxPythonで言うと)受信したすべての行でイベントを発生させるか、今のところ単にそれらを出力します。

Popenでプロセスを作成しましたが、communicate()を使用すると、プロセスが終了すると、データが一度に届きます。

_myprocess.stdout_のreadline()をブロックする別のスレッドを作成すると(_stdout = subprocess.PIPE_を使用)、プロセスが終了するまで、このメソッドでも行を取得できません。 (bufsizeとして何を設定しても)

恐ろしくなく、複数のプラットフォームでうまく機能するこれに対処する方法はありますか?

46
Ryan

動作しないように見えるコードで更新します(とにかくWindowsで)

class ThreadWorker(threading.Thread):
    def __init__(self, callable, *args, **kwargs):
        super(ThreadWorker, self).__init__()
        self.callable = callable
        self.args = args
        self.kwargs = kwargs
        self.setDaemon(True)

    def run(self):
        try:
            self.callable(*self.args, **self.kwargs)
        except wx.PyDeadObjectError:
            pass
        except Exception, e:
            print e



if __== "__main__":
    import os
    from subprocess import Popen, PIPE

    def worker(pipe):
        while True:
            line = pipe.readline()
            if line == '': break
            else: print line

    proc = Popen("python subprocess_test.py", Shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)

    stdout_worker = ThreadWorker(worker, proc.stdout)
    stderr_worker = ThreadWorker(worker, proc.stderr)
    stdout_worker.start()
    stderr_worker.start()
    while True: pass
8
Ryan

stdoutはバッファリングされるため、そのバッファがいっぱいになるか、サブプロセスが終了するまで、何も取得されません。

サブプロセスからstdoutをフラッシュするか、stderrを使用するか、非バッファモードでstdoutを変更してみてください。

7
Douglas Leeder

これが私のために働いたものです:

_cmd = ["./tester_script.bash"]
p = subprocess.Popen( cmd, Shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
while p.poll() is None:
    out = p.stdout.readline()
    do_something_with( out, err )
_

あなたの場合、サブプロセスへの参照をワーカースレッドに渡して、スレッド内でポーリングを実行することができます。 2つのスレッドが同じサブプロセスをポーリング(および相互作用)したときにどのように動作するかはわかりませんが、機能する可能性があります。

また、while p.poll() is None:は現状のままであることに注意してください。実行not python _0_(正常終了のリターンコード)もFalseと見なされるように、while not p.poll()に置き換えます。

2
exhuma

問題は、サブプロセスによるバッファリングされた出力の使用にあるようです。比較的少量の出力が作成された場合、サブプロセスが終了するまでバッファリングされる可能性があります。いくつかの背景を見つけることができます ここ

2

一度に1文字ずつ読んでください: http://blog.thelinuxkid.com/2013/06/get-python-subprocess-output-without.html

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()
1
Andres Restrepo

Pexpect [ http://www.noah.org/wiki/Pexpect] を非ブロッキングreadlineで使用すると、この問題が解決します。これは、パイプがバッファリングされているため、アプリの出力がパイプによってバッファリングされているため、バッファがいっぱいになるかプロセスが終了するまでその出力に到達できません。

1
Gabe

私もこの問題に遭遇しています。 stderrも読み取ろうとしているため、問題が発生します。エラーがない場合、stderrから読み取ろうとするとブロックされます。

Windowsでは、ファイル記述子をpoll()する簡単な方法はありません(Winsockソケットのみ)。

したがって、解決策はstderrから読み取ろうとしないことです。

1
khcheng

これはよく知られているPythonの制限、 PEP 3145 および多分他のものを参照してください。

1
MarcH

Subprocess.Popenを使用して、C#プロジェクトの1つの.exeを実行し、出力をPythonファイルにリダイレクトできます。すべての情報をprint()できるようになりました。 (Console.WriteLine()を使用して)C#コンソールからPythonコンソールに出力されます。

Pythonコード:

from subprocess import Popen, PIPE, STDOUT

p = Popen('ConsoleDataImporter.exe', stdout = PIPE, stderr = STDOUT, Shell = True)

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

これにより、作成時に.NETプロジェクトのコンソール出力が1行ずつ取得され、プロジェクトの終了時に囲んでいるwhileループから抜け出します。これは2つのpythonファイルでも機能すると思います。

1
Karan Narula

私はこれにpexpectモジュールを使用しましたが、問題なく動作しているようです。 http://sourceforge.net/projects/pexpect/

0
user176737