サブプロセスとは
サブプロセスは、プログラムのメインプロセスとは独立して実行される独立したプロセスです。Pythonでは、サブプロセスは外部コマンドやスクリプトの実行、他のアプリケーションとのやり取り、計算の並列処理などによく使用されます。
subprocessモジュール
subprocessモジュールは、Python 2.4で導入され、Python 3で改良された、Pythonでサブプロセスを管理する推奨方法です。これは、サブプロセスの作成、操作、制御のためのシンプルで一貫性のあるインターフェースを提供します。この記事では、subprocessモジュールが提供する様々な関数やクラスについて解説します。
外部コマンドの実行
run()関数
run()関数は、subprocessモジュールを使って外部コマンドを実行するもっとも簡単な方法です。コマンドライン引数のリストを取り、指定されたコマンドを実行し、完了するのを待ちます。
import subprocess
result = subprocess.run(["echo", "Hello, World!"])
コマンドの引数
run()関数にコマンドライン引数を渡す場合、各引数をリストの別々のアイテムとして提供することが重要です。これにより、スペースを含む引数が正しく処理されるようになります。
import subprocess
# 間違った書き方:ファイルパスにスペースがあるため、エラーが発生する
result = subprocess.run(["ls", "/path/to directory with spaces"])
# 正しい書き方:各引数はリストの別々のアイテムである
result = subprocess.run(["ls", "/path/to", "directory with spaces"])
エラーと例外の処理
run()関数はデフォルトで、コマンドが非ゼロの終了ステータスを返した場合にCalledProcessError例外を発生させます。この状況を処理するには、checkパラメータを使用できます。
import subprocess
try:
result = subprocess.run(["false"], check=True)
except subprocess.CalledProcessError as e:
print(f"The command failed with error: {e}")
コマンド出力のキャプチャ
標準出力 (stdout)
コマンドの標準出力 (stdout) をキャプチャするには、run()関数にcapture_outputパラメータを使用します。
import subprocess
result = subprocess.run(["echo", "Hello, World!"], capture_output=True)
output = result.stdout.decode('utf-8')
print(output)
標準エラー (stderr)
同様に、コマンドの標準エラー (stderr) をキャプチャするには、capture_outputパラメータをTrueに設定し、返されたオブジェクトのstderr属性にアクセスします。
import subprocess
result = subprocess.run(["command_that_may_fail"], capture_output=True)
error_output = result.stderr.decode('utf-8')
print(error_output)
出力のリダイレクト
コマンドの出力をファイルにリダイレクトするには、run()関数でstdoutとstderrパラメータを使用します。
import subprocess
with open("output.txt", "w") as output_file:
subprocess.run(["echo", "Hello, World!"], stdout=output_file)
stdoutとstderrを結合
stdoutとstderrを単一の出力にキャプチャするには、stderrパラメータを使用し、subprocess.STDOUTに設定します。
import subprocess
result = subprocess.run(["command_with_both_outputs"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
combined_output = result.stdout.decode('utf-8')
print(combined_output)
発展的ななサブプロセス管理
Popenクラス
Popenクラスは、run()関数に比べてサブプロセスをより細かく制御することができます。Popenを使用すると、実行中のプロセスとやり取りし、その入力にデータを送信し、出力からリアルタイムでデータを読み取ることができます。以下は、Popenを使用してコマンドを開始する例です。
import subprocess
process = subprocess.Popen(["command", "arg1", "arg2"])
プロセスの属性とメソッド
Popenインスタンスには、実行中のプロセスを管理するためのいくつかの属性とメソッドがあります。
pid: 実行中のサブプロセスのプロセスID。returncode: プロセスの返却コード(完了した場合)、そうでない場合はNone。wait(): プロセスの完了を待ち、返却コードを返します。poll(): プロセスが完了しているかどうかを確認し、完了している場合は返却コードを返します。それ以外の場合はNoneを返します。
import subprocess
process = subprocess.Popen(["command", "arg1", "arg2"])
process.wait()
print(f"Process return code: {process.returncode}")
プロセスのやり取り
Popenクラスを使用すると、プロセスの標準入力(stdin)にデータを送信し、標準出力(stdout)と標準エラー(stderr)からデータを読み取ることができます。これを行うには、stdin、stdout、stderrパラメータをsubprocess.PIPEに設定します。
import subprocess
process = subprocess.Popen(["command", "arg1", "arg2"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Send data to stdin
process.stdin.write(b"input data")
process.stdin.flush()
# Read data from stdout and stderr
output = process.stdout.read()
error_output = process.stderr.read()
# Close the input and wait for the process to finish
process.stdin.close()
process.wait()
タイムアウトとプロセスの終了
タイムアウトの設定
サブプロセスのタイムアウトを設定するには、run()関数またはPopenインスタンスのwait()メソッドでtimeoutパラメータを使用します。
import subprocess
try:
result = subprocess.run(["command", "arg1", "arg2"], timeout=5)
except subprocess.TimeoutExpired:
print("The command took too long to complete")
# Alternatively, with Popen
process = subprocess.Popen(["command", "arg1", "arg2"])
try:
process.wait(timeout=5)
except subprocess.TimeoutExpired:
print("The command took too long to complete")
プロセスの終了
実行中のサブプロセスを終了するには、Popenインスタンスのterminate()メソッドを使用します。
import subprocess
process = subprocess.Popen(["command", "arg1", "arg2"])
process.terminate()
プロセスとのやり取り
Popenインスタンスのcommunicate()メソッドを使用すると、プロセスのstdinにデータを送信し、stdoutとstderrからデータを読み取ることができます。このメソッドは、デッドロックのリスクを回避しながらデータを送受信する必要がある場合に役立ちます。
import subprocess
process = subprocess.Popen(["command", "arg1", "arg2"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error_output = process.communicate(input=b"input data")
print(f"Output: {output.decode('utf-8')}")
print(f"Error Output: {error_output.decode('utf-8')}")
参考