物理の駅 Physics station by 現役研究者

テクノロジーは共有されてこそ栄える

Pythonでデッドロックを回避しながらサブプロセスの標準出力を1行ずつ読み込む

Windowsだとシステムからの文字コードshift_jisなのでそれに対応

stderr = subprocess.PIPE にすると、 proc.stdout.readline を先に書いたとき、 stderr側での出力が貯まりすぎると、デッドロックが発生することがあるらしい。なので、stderrもSTDOUT側に流して、 stdout.readline だけで1行ずつ読み込む。

import subprocess
import sys

def run(exe_path,current_dir=None,*, parse=None, silent_pre=False, silent_cout=False,end="\n"):
    if not silent_pre:
        print("Exe:",exe_path)
        if current_dir != None:
            print("Target:",current_dir)

    if current_dir != None:
        proc = subprocess.Popen(exe_path,cwd=current_dir, stdout = subprocess.PIPE, stderr = subprocess.STDOUT,shell=True)
    else:
        proc = subprocess.Popen(exe_path,stdout = subprocess.PIPE, stderr = subprocess.STDOUT,shell=True)

    for line in iter(proc.stdout.readline,b''):
        if silent_cout:continue
        try:line_str = line.rstrip().decode("utf8")
        except:line_str = line.rstrip().decode("shift_jis")
        if parse != None and parse(line_str) == False:continue
        print(line_str,end=end)
    proc.wait()
    ret = int(proc.returncode)
    if  ret != 0:
        print("Return:",ret,"from",exe_path,"in",current_dir)

    return ret 

完全に自信はない。これで良い?

communicate() を使うと一気にstdoutもstderrも読めるらしいけど、リアルタイム表示したいので使えない。

簡略化したコード

def run(exe_path):
    proc = subprocess.Popen(exe_path,stdout = subprocess.PIPE, stderr = subprocess.STDOUT,shell=True)
    for line in iter(proc.stdout.readline,b''):
        try:line_str = line.rstrip().decode("utf8")
        except:line_str = line.rstrip().decode("shift_jis")
        print(line_str,end="\n")
    proc.wait()
    ret = int(proc.returncode)
    if  ret != 0:
        print("Return:",ret,"from",exe_path)
    return ret 

参照

Pythonでサブプロセスの標準出力を1行ずつ読み込む - Qiita

Python の subprocess で出力を受け取るときは communicate() を使おう - Qiita