39

アプリの出力が少量の場合は正常に動作するが、出力が多い場合はハングする外部アプリを実行する Python コードがいくつかあります。私のコードは次のようになります:

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
errcode = p.wait()
retval = p.stdout.read()
errmess = p.stderr.read()
if errcode:
    log.error('cmd failed <%s>: %s' % (errcode,errmess))

ドキュメントには、潜在的な問題を示していると思われるコメントがあります。待機中は、次のとおりです。

警告: 子プロセスがstdoutまたはstderrパイプに十分な出力を生成し、OS パイプ バッファーがさらにデータを受け入れるのを待機するのをブロックする場合、これはデッドロックになります。communicate()それを回避するために使用します。

通信中ですが、次のように表示されます。

注 読み取ったデータはメモリにバッファリングされるため、データ サイズが大きい場合や無制限の場合は、このメソッドを使用しないでください。

したがって、大量のデータがある場合、これらのいずれかを使用する必要があるかどうかは不明です。その場合にどの方法を使用すべきかは示していません。

exec からの戻り値が必要であり、 と の両方を解析して使用しstdoutますstderr

では、大きな出力を持つ外部アプリを実行するための Python での同等の方法は何でしょうか?

4

7 に答える 7

19

2 つのファイルに対して読み取りをブロックしています。2 番目が開始する前に、1 番目が完了する必要があります。アプリケーションが に大量の書き込みをstderr行い、 に何も書き込みを行わないstdout場合、プロセスはデータがstdout来ないのを待って待機し、実行中のプログラムは書き込まれたデータstderrが読み取られるのを待機します (これは絶対にありません)。になります--あなたが待っているのでstdout)。

これを修正する方法はいくつかあります。

最も簡単なのは、インターセプトしないことstderrです。残すstderr=None。エラーはstderr直接出力されます。それらを傍受して、独自のメッセージの一部として表示することはできません。コマンドライン ツールの場合、これで問題ないことがよくあります。他のアプリの場合、問題になる可能性があります。

もう 1 つの簡単な方法は、にリダイレクトstderrするstdoutことです。つまり、着信ファイルは set だけstderr=STDOUTです。これは、通常の出力とエラー出力を区別できないことを意味します。これは、アプリケーションが出力を書き込む方法に応じて、許容される場合と許容されない場合があります。

これを処理する完全で複雑な方法はselect( http://docs.python.org/library/select.html ) です。stdoutこれにより、ブロックしない方法で読み取ることができます。データが または のいずれかに表示されるたびにデータを取得しますstderr。本当に必要な場合にのみお勧めします。これはおそらく Windows では機能しません。

于 2009-07-24T23:23:35.690 に答える
8

を使用して、非常に大きな出力 (つまり、大量のメガバイト) を個別にstdout読み取ります。stderrselect

import subprocess, select

proc = subprocess.Popen(cmd, bufsize=8192, shell=False, \
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

with open(outpath, "wb") as outf:
    dataend = False
    while (proc.returncode is None) or (not dataend):
        proc.poll()
        dataend = False

        ready = select.select([proc.stdout, proc.stderr], [], [], 1.0)

        if proc.stderr in ready[0]:
            data = proc.stderr.read(1024)
            if len(data) > 0:
                handle_stderr_data(data)

        if proc.stdout in ready[0]:
            data = proc.stdout.read(1024)
            if len(data) == 0: # Read of zero bytes means EOF
                dataend = True
            else:
                outf.write(data)
于 2016-12-02T09:49:55.473 に答える
6

私も同じ問題を抱えていました。大量の出力を処理する必要がある場合は、stdout と stderr にファイルを使用し、それらのファイルをパラメーターごとに渡すという別の適切なオプションがあります。

Python の tempfile モジュールを確認してください: https://docs.python.org/2/library/tempfile.html

このようなものがうまくいくかもしれません

out = tempfile.NamedTemporaryFile(delete=False)

次に、次のようにします。

Popen(... stdout=out,...)

その後、ファイルを読み取って、後で消去できます。

于 2014-07-24T20:28:30.003 に答える
6

Glenn Maynard のデッドロックに関するコメントは正しいです。ただし、この問題を解決する最善の方法は、stdout 用と stderr 用の 2 つのスレッドを作成することです。これらのスレッドは、それぞれのストリームを使い果たすまで読み取り、出力に対して必要なことは何でも行います。

一時ファイルを使用するという提案は、出力のサイズなど、および生成されたサブプロセスの出力を処理する必要があるかどうかによって、うまくいく場合とうまくいかない場合があります。

Heikki Toivonen が提案したように、communicateメソッドを確認する必要があります。ただし、これはサブプロセスの stdout/stderr をメモリにバッファリングし、communicate呼び出しから返されたものを取得します。これは、一部のシナリオでは理想的ではありません。しかし、communication メソッドのソースは一見の価値があります。

もう 1 つの例は、私が管理しているパッケージpython-gnupgにあります。このパッケージでは、重い作業を行うためにgpg実行可能ファイルが生成さsubprocessれ、Python ラッパーがスレッドを生成して gpg の stdout と stderr を読み取り、gpg によってデータが生成されるときにそれらを消費します。そこにあるソースを見ることで、いくつかのアイデアを得ることができるかもしれません. 一般的に、gpg によって stdout と stderr の両方に生成されるデータは非常に大きくなる可能性があります。

于 2009-07-25T19:14:53.703 に答える
6

出力の多くは主観的なものなので、推奨するのは少し難しいです。出力の量が非常に多い場合は 1 回の read() 呼び出しですべてを取得したくない可能性があります。出力をファイルに書き込んでから、次のように段階的にデータを取得することをお勧めします。

f=file('data.out','w')
p = subprocess.Popen(cmd, shell=True, stdout=f, stderr=subprocess.PIPE)
errcode = p.wait()
f.close()
if errcode:
    errmess = p.stderr.read()
    log.error('cmd failed <%s>: %s' % (errcode,errmess))
for line in file('data.out'):
    #do something
于 2009-07-24T23:18:34.077 に答える
2

通信してみて、問題が解決するかどうかを確認してください。そうでない場合は、出力を一時ファイルにリダイレクトします。

于 2009-07-24T23:24:10.260 に答える