はじめに
Pythonのsubprocessでコマンド実行時
- その標準出力を文字列として取得したい
- 同時に、コンソールにもリアルタイム表示したい
といった場合の実装例です。
仕様
- MyConsoleクラス
- パイプを作成し、読込み用と書込み用のファイルディスクリプタをクラスメンバーに指定
- スレッドを作成し、下記処理を行う
- パイプにデータ存在するまで読込む(4KB単位)
- 取得データをシステム標準出力に書き込む
- 同時に、データを結果文字列にも追加
- subprocess.run()でコマンド実行
- stdoutパラメータにMyConsole書込み用ファイルディスクリプタを指定
コード
Windowsのサンプルコードです。
test_console.py
import threading
import os
import sys
import subprocess
class MyConsole:
def __init__(self):
self.result_str = ""
self.rfd, self.wfd = os.pipe()
thread = threading.Thread(target=self._read)
thread.daemon = True
thread.start()
def _read(self):
while True:
data = os.read(self.rfd, 4096)
if data is None:
break
data_str = data.decode()
sys.stdout.write(data_str)
self.result_str += data_str
os.close(self.rfd)
os.close(self.wfd)
def get(self):
return self.result_str
if __name__ == "__main__":
con = MyConsole()
print("==== ここから標準出力のコンソール表示")
subprocess.run(["ping", "127.0.0.1", "-n", "3"], shell=True, stdout=con.wfd)
print("==== ここまで標準出力のコンソール表示\n")
print("==== ここから標準出力から取得した結果文字列")
print(con.get())
print("==== ここまで標準出力から取得した結果文字列")
実行結果
> python .\test_console.py
==== ここから標準出力のコンソール表示
Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Ping statistics for 127.0.0.1:
Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
==== ここまで標準出力のコンソール表示
==== ここから標準出力から取得した結果文字列
Pinging 127.0.0.1 with 32 bytes of data:
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
Ping statistics for 127.0.0.1:
Packets: Sent = 3, Received = 3, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
==== ここまで標準出力から取得した結果文字列
同じ内容が、コンソールに表示されつつ、
結果文字列にも、保存されます。
Linuxの場合
サンプルコードで、下記1行を書き換えてください。
- 修正前
subprocess.run(["ping", "127.0.0.1", "-n", "3"], shell=True, stdout=con.wfd)
- 修正後
subprocess.run(["ping", "127.0.0.1", "-c", "3"], stdout=con.wfd)
おわりに
Pythonのsubprocessで標準出力を制御してみました。
subprocess使用時に、ご参考になれば何よりです。