web-dev-qa-db-ja.com

stdoutをsubprocess.Popenからファイルに保存し、さらにファイルにさらに書き込む

python subprocess.Popenを使用して2つのプログラム(コンパイル済みCコードから)を実行し、それぞれが標準出力を生成するスクリプトを記述しています。スクリプトはその出力を取得し、ファイルに保存します。出力がsubprocess.PIPEを圧倒するほど大きい場合があり、スクリプトがハングします。stdoutを直接ログファイルに送信します。スクリプトにファイルの先頭と末尾、および2つのサブプロセスの間に何かを書き込ませます。 Popenを呼び出しますが、ログファイルを見ると、スクリプトからログファイルに書き込んだ内容はすべてファイルの先頭にまとめられ、その後にすべての実行可能stdoutが続きます。 ?

def run(cmd, logfile):
    p = subprocess.Popen(cmd, Shell=True, universal_newlines=True, stdout=logfile)
    return p

def runTest(path, flags, name):
    log = open(name, "w")
    print >> log, "Calling executable A"
    a_ret = run(path + "executable_a_name" + flags, log)
    print >> log, "Calling executable B"
    b_ret = run(path + "executable_b_name" + flags, log)
    print >> log, "More stuff"
    log.close()

ログファイルには以下が含まれています。実行可能ファイルの呼び出しA実行可能ファイルの呼び出しBその他

たとえば、Popenを呼び出した後、Aの標準出力をログにフラッシュする方法はありますか?関連するもう1つのこと:実行可能ファイルAが起動してからBに保留し、Bが印刷して終了した後、Aがさらに印刷して終了します。

RHE LinuxではPython 2.4を使用しています。

20
jasper77

各Popenオブジェクトで.wait()を呼び出して、終了したことを確認してからlog.flush()を呼び出すことができます。たぶんこのようなもの:

def run(cmd, logfile):
    p = subprocess.Popen(cmd, Shell=True, universal_newlines=True, stdout=logfile)
    ret_code = p.wait()
    logfile.flush()
    return ret_code

外部関数でPopenオブジェクトと対話する必要がある場合は、代わりに.wait()呼び出しをそこに移動できます。

21
Benno

私が理解しているように、AプログラムはBが実行するのを待ち、ABが終了した後にのみ終了します。

BAを実行せずに開始できる場合、逆の順序でプロセスを開始できます。

_from os.path import join as pjoin
from subprocess import Popen

def run_async(cmd, logfile):
    print >>log, "calling", cmd
    p = Popen(cmd, stdout=logfile)
    print >>log, "started", cmd
    return p

def runTest(path, flags, name):
    log = open(name, "w", 1)  # line-buffered
    print >>log, 'calling both processes'
    pb = run_async([pjoin(path, "executable_b_name")] + flags.split(), log)
    pa = run_async([pjoin(path, "executable_a_name")] + flags.split(), log)
    print >>log, 'started both processes'
    pb.wait()
    print >>log, 'process B ended'
    pa.wait()
    print >>log, 'process A ended'
    log.close()
_

注:メインプロセスでlog.flush()を呼び出しても、子プロセスのファイルバッファには影響しません。

子プロセスがstdoutにブロックバッファリングを使用する場合、 pexpect、pty、またはstdbuf を使用して、より早くフラッシュするように強制することができます(インタラクティブに実行する場合、またはC I/O用のstdioライブラリ)。

2
jfs

続行するには、プロセスが完了するまで待つ必要があります。また、よりクリーンなコンテキストマネージャーを使用するようにコードを変換しました。

def run(cmd, logfile):
    p = subprocess.Popen(cmd, Shell=True, universal_newlines=True, stdout=logfile)
    p.wait()
    return p

def runTest(path, flags, name):
    with open(name, "w") as log:
        print >> log, "Calling executable A"
        a_ret = run(path + "executable_a_name" + flags, log)
        print >> log, "Calling executable B"
        b_ret = run(path + "executable_b_name" + flags, log)
        print >> log, "More stuff"
2
Chris B.

私はそれを本当にシンプルに保つと言います。擬似コードの基本ロジック:

write your start messages to logA
execute A with output to logA
write your in-between messages to logB
execute B with output to logB
write your final messages to logB
when A & B finish, write content of logB to the end of logA
delete logB
2
Peter Lyons