実行中に Python サブプロセスが Stdout を読み取る

Salman Mehmood 2023年10月8日
  1. 実行中に Python サブプロセスが stdout を読み取る
  2. アプローチ 1: Python での実行中に check_call を使用して subprocessstdout を読み取る
  3. アプローチ 2: プロセスをポーリングして、Python で実行中に subprocessstdout を読み取る
実行中に Python サブプロセスが Stdout を読み取る

この記事の主な目的は、Python で実行されている subprocessstdout を読み取る方法を示すことです。

実行中に Python サブプロセスが stdout を読み取る

他の多くの組み込みモジュールと同様に、Subprocess も組み込みモジュールであり、通常のPython インストールでプリインストールされています。

これは主に、新しいプロセスでタスク、プロセス、およびプログラムを実行し、特定の一連のタスクを実行して結果を返したい場合に使用されます。

広く使用されている多くの理由の 1つは、外部プログラムと実行可能ファイルをプログラムから直接別のプロセスとして実行できることです。

Subprocess ライブラリを使用してプログラムを実行している間、その外部プログラムの出力をリアルタイムで表示する必要がある場合があります。 これは、プログラムがリアルタイムで、短時間の計算に依存する場合など、さまざまな理由で要件になる可能性があります。

次のプログラムを検討してください。

import subprocess


def execute(command):
    process = subprocess.Popen(
        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
    )
    output = process.communicate()[0]
    exitCode = process.returncode

    if exitCode == 0:
        return output
    else:
        raise Exception(command, exitCode, output)


if __name__ == "__main__":
    print(execute("cd C:\\ && C: && tree").decode("unicode_escape"))

出力:

Folder PATH listing
Volume serial number is 0213-B7F2
C:.
+---DRIVERS
¦   +---bluetooth
¦       +---U1BLT07AVVASDSAP
¦           +---Custom
¦           ¦   +---EULA
¦           +---Win64
¦               +---LD
¦               +---svcpack
+---flutter
¦   +---.git
¦   ¦   +---hooks
¦   ¦   +---info
¦   ¦   +---logs
¦   ¦   ¦   +---refs
¦   ¦   ¦       +---heads
¦   ¦   ¦       +---remotes
¦   ¦   ¦           +---origin
¦   ¦   +---objects
¦   ¦   ¦   +---info
¦   ¦   ¦   +---pack
¦   ¦   +---refs
¦   ¦       +---heads
¦   ¦       +---remotes
¦   ¦       ¦   +---origin
¦   ¦       +---tags
¦   +---.github
¦   ¦   +---ISSUE_TEMPLATE
¦   ¦   +---workflows
¦   +---.idea
¦   ¦   +---runConfigurations
...

プログラムで出力を確認できますが、リアルタイムでは確認できません。 出力は、コマンド全体 (この場合は tree) の実行が終了した後にのみ表示されます。

プログラム (またはコマンド) が Subprocess コマンドを使用して別のプロセスで実行されるまで、出力には何も表示されません。

私たちの場合、出力をリアルタイムで取得する必要があるため、別のソリューションを作成する必要があります。これは、stdout に書き込まれたプログラムの出力を表示するソリューションです。

解決策には複数の方法がありますが、その一部を以下に示します。

アプローチ 1: Python での実行中に check_call を使用して subprocessstdout を読み取る

次のコードを検討してください。

import subprocess
import sys


def execute(command):
    subprocess.check_call(
        command, shell=True, stdout=sys.stdout, stderr=subprocess.STDOUT
    )


if __name__ == "__main__":
    print(execute("cd C:\\ && C: && tree").decode("unicode_escape"))

出力:

Folder PATH listing
Volume serial number is 0213-B7F2
C:.
├───DRIVERS
│   └───bluetooth
│       └───U1BLT0720US14CMP
│           ├───Custom
│           │   └───EULA
│           └───Win64
│               ├───LD
│               └───svcpack
├───flutter
│   ├───.git
│   │   ├───hooks
│   │   ├───info
│   │   ├───logs
│   │   │   └───refs
│   │   │       ├───heads
│   │   │       └───remotes
│   │   │           └───origin
│   │   ├───objects
│   │   │   ├───info
│   │   │   └───pack
│   │   └───refs
│   │       ├───heads
│   │       ├───remotes
│   │       │   └───origin
│   │       └───tags
│   ├───.github
│   │   ├───ISSUE_TEMPLATE
│   │   └───workflows
│   ├───.idea
│   │   └───runConfigurations
│   ├───.pub-cache
│   │   ├───hosted
│   │   │   └───pub.dartlang.org
│   │   │       ├───.cache
...

主な要件がプログラムの出力をリアルタイムで出力することである場合、check_call を使用してこれを実現できます。 このシンプルでクリーンでエレガントなソリューションは、出力のみを印刷する必要がある場合など、非常に単純なプログラムにとって簡潔で完璧です。

check_call はパラメーターの受け渡しもサポートしているため、プログラムが機能するために引数が必要な場合は、手間をかけずに簡単に関数に渡すことができます。 このメソッドは、プログラムが完了するまで待機します。

プログラムの完了に基づいて、メソッドは戻ります。 それ以外の場合は、CalledProcessError 例外が発生します。 CalledProcessError には、returncode 属性を使用してアクセスできる失敗の戻りコードがあります。

上記のコードでは、commandcheck_call メソッドに渡されます。このメソッドには、実行するコマンド (またはプログラム) が含まれています。

シェルを使用してプロセスを実行するために shell パラメータは true に設定され、プロセスの stdout はプログラムの stdout に設定されているため、stdout に直接書き込みます。 stdout で発生する変更を確認します。

最後に、生成されたプロセスの stdoutstderr が設定されます。

アプローチ 2: プロセスをポーリングして、Python で実行中に subprocessstdout を読み取る

次のコードを検討してください。

import subprocess
import sys


def execute(command):
    process = subprocess.Popen(
        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
    )

    # Poll process for new output until finished
    while True:
        nextline = process.stdout.readline().decode("unicode_escape")
        if nextline == "" and process.poll() is not None:
            break
            sys.stdout.write(nextline)
            sys.stdout.flush()

        output = process.communicate()[0]
        exitCode = process.returncode

        if exitCode == 0:
            return output
        else:
            raise Exception(command, exitCode, output)


if __name__ == "__main__":
    print(execute("cd C:\\ && C: && tree").decode("unicode_escape"))

出力:

...
¦   ¦   ¦       ¦   ¦       +---UuidUtil
¦   ¦   ¦       ¦   +---example
¦   ¦   ¦       ¦   +---lib
¦   ¦   ¦       ¦   +---test
¦   ¦   ¦       +---vector_math-2.1.2
¦   ¦   ¦       ¦   +---benchmark
¦   ¦   ¦       ¦   +---bin
¦   ¦   ¦       ¦   +---lib
¦   ¦   ¦       ¦   ¦   +---src
¦   ¦   ¦       ¦   ¦       +---vector_math
¦   ¦   ¦       ¦   ¦       ¦   +---third_party
¦   ¦   ¦       ¦   ¦       +---vector_math_64
¦   ¦   ¦       ¦   ¦       ¦   +---third_party
¦   ¦   ¦       ¦   ¦       +---vector_math_geometry
¦   ¦   ¦       ¦   ¦       ¦   +---filters
¦   ¦   ¦       ¦   ¦       ¦   +---generators
¦   ¦   ¦       ¦   ¦       +---vector_math_lists
¦   ¦   ¦       ¦   ¦       +---vector_math_operations
¦   ¦   ¦       ¦   +---test
¦   ¦   ¦       ¦   +---tool
¦   ¦   ¦       +---video_player-2.2.11
¦   ¦   ¦       ¦   +---android
¦   ¦   ¦       ¦   ¦   +---gradle
...

Subprocess を使用して生成されたプログラムの出力が stdout に書き込まれるとすぐに印刷されるようにするには、プロセスをポーリングして出力し、プログラムの stdoutstdout の最後の行を読み続ける必要があります。

無限ループでは、生成されたプロセスの出力を readline() を使用して読み取り続けます。 出力はエンコードされているため、適切に表現するにはデコードする必要があります (この場合は utf-escape)。

これは、decode メソッドを使用して、関連するエンコード スキームを渡すことで実行できます。

プログラムの実行が終了したら、無限ループもエスケープする必要があります。 これを行うには、次の 2つのことを確認します。

  1. 現在の stdout 行は空です。
  2. プロセスが終了または終了しました。

これは、最初の条件の単純な文字列比較と、プロセスが終了したかどうかを確認する poll() メソッドを使用して、非常に簡単に実行できます。 プログラムが終了した場合は、returncode を返します。 それ以外の場合、None を返します。

None との単純な比較でも、プログラムの実行が終了したかどうかに関する情報を得ることができます。

著者: Salman Mehmood
Salman Mehmood avatar Salman Mehmood avatar

Hello! I am Salman Bin Mehmood(Baum), a software developer and I help organizations, address complex problems. My expertise lies within back-end, data science and machine learning. I am a lifelong learner, currently working on metaverse, and enrolled in a course building an AI application with python. I love solving problems and developing bug-free software for people. I write content related to python and hot Technologies.

LinkedIn

関連記事 - Python Subprocess