HOWTO · Python
Python で外部プログラムを呼び出す
このチュートリアルでは、Python で外部プログラムを実行する方法を示します
このページの内容
通常の外部コマンドのほとんどには subprocess.run() を使ってください。実行ファイルとその引数はリストとして渡し、Python がシェルに解釈させないようにします。読みやすい出力が必要なときは capture_output=True と text=True を設定し、完了したプロセスのオブジェクトで stdout、stderr、returncode を確認します。以下の例は Python 自体を起動するため、シェルユーティリティがインストールされているかどうかに依存しません。
この例を実行します:
このセクションのコードと、表示されるラベルや出力は英語のままです。
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "print('Hello from Python')"],
capture_output=True,
text=True,
check=True,
)
print(result.stdout, end="")
print("Return code:", result.returncode)
Hello from Python
Return code: 0
リストを使うと、引数内部の空白を含め、各引数が分離された状態に保たれます。text=True は、プラットフォームのテキストエンコーディングを使って、キャプチャしたストリームを文字列にデコードします。これがない場合、stdout と stderr はバイト列になります。check=True を指定すると、コマンドのステータスが 0 以外のときに subprocess.CalledProcessError が発生します。ステータスを自分で処理したい場合は、この指定を外してください。キャプチャした出力はコマンドのステータスとは別物です。コマンドはどちらのストリームにも書き込むことができ、なおかつ 0 以外の returncode で終了することがあります。
シェルコマンドの実行には os.system() を使う
os.system() は、その文字列をコマンドプロセッサ(一般的なプラットフォームではシェル)に通して実行し、コマンドプロセッサのステータスを返します。コマンドの標準出力と標準エラー出力は通常、親プロセスに接続されたままになるため、この呼び出しでキャプチャしたテキストは得られません。これは、シェルの動作を意図的に使う場合に、単純なシェルコマンド向けのレガシーな選択肢です。引数リスト、キャプチャしたストリーム、または構造化されたステータス処理が必要な場合は、subprocess.run() を優先してください。
入力はシェル構文であるため、信頼できないテキストを挿入して文字列を組み立ててはいけません。シェルのメタ文字によって、実行される内容が変わる可能性があります。正確なコマンドプロセッサやステータスのエンコーディングはプラットフォームによって異なるため、戻り値が常に子プログラムの終了コードそのものだと想定しないでください。たとえば、ポータブルな概念上の呼び出しは os.system("some-command") です。実行するオペレーティングシステムに合わせて、具体的なコマンドと引用規則を選んでください。
コマンドの出力を読み取るには os.popen() を使う
os.popen() は、シェルを通じて実行されるコマンドへのパイプを開き、ファイルに似たオブジェクトを返します。ストリームを読み取り、終わったら閉じてください。close() はコマンドのステータスも提供できます。このインターフェースは、単純なパイプベースのやり取り向けのレガシーな選択肢であり、subprocess で利用できる stdout と stderr の分離した処理は提供しません。
たとえば、その形は pipe = os.popen(command) で、その後に output = pipe.read() と status = pipe.close() が続きます。コマンド文字列はシェルによって解釈されるため、信頼できない入力には os.system() と同じ注意を払ってください。close() の戻り値はキャプチャした出力の代わりにはならず、そのステータスの表現はプラットフォームに依存することがあります。出力と明確なリターンコードの両方が必要な新しいコードには、subprocess.run() を優先してください。
より低レベルなプロセス制御には subprocess.Popen() を使う
subprocess.Popen() は、プログラムが実行中のプロセスとやり取りする必要がある場合に使うクラスコンストラクタです。直接実行するには引数リストを渡します。stdout=subprocess.PIPE または stderr=subprocess.PIPE は、それらのストリームを読む予定がある場合にだけ設定してください。設定したパイプを収集して完了を待つには communicate() を呼び、パイプ出力の収集が不要な場合は wait() を呼びます。
子プロセスがもう一方のパイプに大量に書き込んでいる間に一方のパイプを手動で読むと、オペレーティングシステムのバッファが満杯になり、プロセスが停止することがあります。communicate() は、両方のストリームをまとめて収集する通常の方法です。テキストモードが有効でない限り、バイト列を返します。出力が大きい場合やストリーミングされる場合は、すべてをメモリにバッファするのではなく、ストリームを意図的に処理してください。待機して完了した結果を確認するだけなら、subprocess.run() のほうが簡単です。
コマンドを実行して待機するには subprocess.call() を使う
subprocess.call() はコマンドを実行し、終了するまで待機して、そのステータスを返します。シェルによる解釈を避けるため、subprocess.call(["program", "--version"]) のようなリストを指定してください。この便利関数はデフォルトでは出力をキャプチャせず、子プロセスのストリームは継承されます。キャプチャしたストリーム、テキストのデコード、または CompletedProcess のフィールドが必要な場合は、subprocess.run() を使ってください。call() は、既存のコードが同期的なステータスだけを必要とする場合には引き続き有用ですが、新しいコードでは通常、より明確な run() インターフェースのほうが有益です。
ステータス 0 は一般に成功を示し、0 以外のステータスはコマンドが問題を報告したことを示します。特定の 0 以外の値の意味は呼び出されたプログラムに属し、ステータスの慣例はプラットフォームによって異なることがあります。0 以外のステータスをエラーとして扱う必要がある場合、subprocess.run(..., check=True) は CalledProcessError を発生させます。そうでない場合は returncode を確認し、アプリケーションがどう対応すべきかを決めてください。
ほとんどの外部コマンドには subprocess.run() を使う
通常のタスクでは、subprocess.run() は完了まで待機し、CompletedProcess を返します。capture_output=True を指定すると、結果には stdout と stderr が含まれ、text=True を指定すると、それらの値は文字列になります。この例は標準出力とリターンコードを表示し、標準エラー出力は result.stderr で引き続き利用できます。プログラムが出力を標準エラー出力に書き込む場合、それは result.stdout には現れません。
引数リストを優先し、プログラムを直接呼び出す場合はデフォルトの shell=False のままにしてください。これにより、引数内のシェルのメタ文字がシェル構文として解釈されるのを防げます。ただし、対象プログラム自体にも入力やオプション処理のリスクはあります。shell=True は、パイプラインやリダイレクトなどのシェル機能が必要な場合にだけ使い、その場合はシェルコマンドを固定するか、プラットフォームに適した慎重な引用で組み立ててください。引数リストはポータブルなデフォルトですが、実行ファイル名やコマンドラインオプションはオペレーティングシステムによって依然として異なります。
コマンドが失敗したときは、その結果をどう扱うか選んでください。check=True を使えば CalledProcessError で停止し、false のままにすれば result.returncode で分岐できます。実行ファイルが見つからない、またはプロセスを開始できない場合は、完了した結果が存在する前にオペレーティングシステムのエラーが発生します。出力テキストだけから成功を推測してはいけません。ステータスを使い、必要に応じて両方のストリームを調べてください。
まとめ
ほとんどの外部コマンドには subprocess.run() を使ってください。引数を分離した状態に保ち、プロセスの完了を待ち、ステータスとキャプチャしたストリームを含む結果が得られます。プロセスの実行中に低レベルなやり取りが必要な場合は subprocess.Popen() を使ってください。subprocess.call() は、ステータスのみを返す小さな同期的な便利関数です。os.system() と os.popen() はシェルベースのレガシーなインターフェースです。それらの動作がタスクに合う場合にだけ選んでください。シェルへの入力は信頼できるものに保ち、必要なストリームだけをキャプチャし、実行ファイル名やステータスの慣例がプラットフォームによって異なることを考慮してください。