LoginSignup
33
29

More than 5 years have passed since last update.

subprocess.Popenで実行したコマンドの出力を徐々に表示する

Last updated at Posted at 2014-11-13

subprocessモジュールを使って外部コマンドを実行する際には、communicateメソッドをよく使用していた。

subprocess.Popen.communicate()
import subprocess

(stdoutdata, stderrdata) = subprocess.Popen(['some', 'command'], stdout=subprocess.PIPE).communicate()
print(stdoutdata)

しかしビルドスクリプトなど、結果を非同期に出力したい場合はcommunicateメソッドでは実現できない。ライブラリリファレンスにあるとおり、communicateメソッドはプロセス終了まで待ち、その後に標準出力のデータを返すからだ。

この要求を実現したい場合、ioモジュールを使って子プロセスの出力に対応したストリームを作成する必要がある。

(ref) http://docs.python.jp/2/library/subprocess.html#subprocess.Popen.stdout
(ref) https://gist.github.com/mattbornski/3299031

子プロセスの出力に対応したストリーム
def exec_process(commands):
    process = subprocess.Popen(commands, stdout=subprocess.PIPE, bufsize=-1)
    with io.open(process.stdout.fileno(), closefd=False) as stream:
        [print(line.rstrip('\n')) for line in stream]

    # プロセス終了まで待ち、結果を判定する
    process.wait()
    if process.returncode != 0:
        print('Build process aborts.')
        sys.exit(1)

以下、何点かハマった点のメモ

1.io.openでストリームを生成する際には、ファイルパスまたはファイル記述子((file object).fileno()で取得可能)を与える必要がある。subprocess.Popen.stdoutのパス名は分からないため、ファイル記述子を与える必要がある。ファイルオブジェクト(=subprocess.Popen.stdout)をそのまま与えるのではないことに注意する。

(ref) http://docs.python.jp/2/library/io.html#io.open

# process.stdoutをio.openに直接与えると実行時例外で終了する。
def exec_process(commands):
    process = subprocess.Popen(commands, stdout=subprocess.PIPE, bufsize=-1)
    with io.open(process.stdout, closefd=False) as stream:
        [print(line.rstrip('\n')) for line in stream]

2.io.openでストリームを生成する際、closefd=Falseを指定する必要がある。デフォルトではclosefd=Trueであり、その状態だと以下のエラーが発生する。

close failed in file object destructor:
IOError: [Errno 9] Bad file descriptor

streamが閉じられるタイミング(=今回の場合、with文を抜ける時)で、子プロセスの掴んでいるファイルオブジェクト(=subprocess.Popen.stdout)を閉じようとすることが原因だと考えられる。

33
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
29