HOWTO · Python

Python で Bash スクリプトを実行する方法

私たちの包括的なガイドを使って、Python を使用して Bash スクリプトを実行する方法を学びましょう。効率的に Bash コマンドを実行するために、subprocess、os.system、および subprocess.Popen のような方法を探求します。Bash スクリプトを Python アプリケーションに統合することで、自動化スキルを向上させ、ワークフローを効率化しましょう。

このページの内容

Pythonの処理を続ける前に完了するBashスクリプトの多くには、subprocess.runを使うのが適しています。引数を個別に渡し、stdoutとstderrを取得して、プロセスのreturncodeを確認できます。Bashがインストールされ、Pythonから利用できる必要があるため、この方法は実行環境に依存し、Bashのないシステムでは使えません。次の例ではスクリプトファイルを作成し、名前を引数として渡して、取得した結果を表示します。

方法1: subprocessモジュールを使う

この自己完結型の例では、小さなBashスクリプトを作成して引数を渡し、2つの出力ストリームを取得します。Bashスクリプトが挨拶を表示し、その後Pythonが取得したテキストとプロセスの状態を表示します。例は変更されないため、表示される挨拶と「Return code」という状態ラベルは英語のままです。

import subprocess
import tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as directory:
    script_path = Path(directory) / "greet.sh"
    script_path.write_text(
        '#!/usr/bin/env bash\nprintf "Hello, %s!\\n" "$1"\n',
        encoding="utf-8",
    )

    result = subprocess.run(
        ["bash", str(script_path), "Ada"],
        capture_output=True,
        text=True,
        check=False,
    )

print(result.stdout, end="")
print(f"Return code: {result.returncode}")
Hello, Ada!
Return code: 0

引数リストでは、スクリプトのパスとAdaを別々の引数として渡します。既定の shell=Falseでは、Pythonはシェルにリストを解析させず、Bashを直接起動します。これにより、それらの引数に対するシェルの解析は避けられますが、あらゆる状況で安全になるわけではありません。入力を検証し、スクリプト自体の動作も考慮してください。returncodeが0なら成功、0以外ならプロセスが失敗を報告したことを示します。stderrは診断情報用の別ストリームであり、そこにテキストが出力されたことだけでコマンドの失敗を意味するわけではありません。capture_output=Trueを指定すると、結果オブジェクトからstdoutとstderrの両方を取得できます。

方法2: os.systemを使う

単純なコマンドで十分で、Python側で出力を取得する必要がない場合に限り、os.systemを使います。コマンド文字列を1つ受け取り、システムのシェルを介して実行し、そのストリームを呼び出し元プロセスの標準ストリームに送ります。戻り値はシステムに依存し、子プロセスの終了コードそのものではなく、プロセス状態を符号化した値の場合があります。そのため、移植可能な終了コードとして扱わないでください。stdoutやstderrを直接取得できる結果オブジェクトもありません。シェルによる解析で入力に特別な意味が与えられる可能性があるため、信頼できない入力からコマンド文字列を組み立てるのは避けてください。引数を個別に渡し、ストリームを取得して状態を確実に調べるには、subprocessを使います。

方法3: subprocess.Popenを使う

実行中のプロセスのライフサイクル管理やパイプの接続など、より細かな制御が必要な場合はsubprocess.Popenを選びます。有限のやり取りでは、プロセスの作成時にstdin=subprocess.PIPEを設定し、その後communicate(input=...)を呼び出して入力を送り、プロセス終了後に取得したストリームを回収します。このstdinパイプがなければ、communicate(input=...)で子プロセスにデータを送ることはできません。また、出力をリアルタイムで受け取ることもできません。両方の出力ストリームにパイプを作成した場合は、communicate()で読み取ってください。パイプがいっぱいになるまで子プロセスを待つと、デッドロックが起きる可能性があります。リアルタイムで出力を受け取るには、communicate()をストリーミング用APIとして扱わず、ストリームを適切に読み取って排出してください。パイプの挙動はプラットフォームとプロセスによって異なります。

結論

最後まで実行するBashスクリプトには、通常subprocess.runが最も分かりやすい選択肢です。引数リストを渡し、必要に応じてストリームを取得し、returncodeで成功を判断できます。出力を扱わない単純なシェルコマンドにはos.systemを使い、プロセスの明示的な制御や通信が必要な場合はsubprocess.Popenを使います。Pythonプログラムの実行環境にBashがインストールされ、利用可能であることを確認してください。PythonがBashの実行ファイルを見つけられなければ、スクリプトを起動できません。

よくある質問

  1. PythonからBashスクリプトを実行する最適な方法は何ですか?
    Pythonの処理を続ける前に終了するスクリプトの多くには、subprocess.runを使います。引数リスト、取得したストリーム、returncodeによるプロセス状態の確認に対応しています。

  2. スクリプトの出力を取得するにはどうすればよいですか?
    subprocess.runにcapture_output=Trueとtext=Trueを指定し、返されたオブジェクトからstdoutとstderrを読み取ります。Popenでは必要なパイプを接続し、communicate()を呼び出して完了後にストリームを回収します。これは出力を取得する方法であり、リアルタイムのストリーミングではありません。プロセスが成功していてもstderrに診断情報が含まれる場合があるため、状態はreturncodeで確認してください。

  3. 引数リストを渡せば安全ですか?
    shell=Falseでは、引数リストに含まれる値がシェルによって解析されるのを避けられます。これによりシェルインジェクションのリスクは下がりますが、すべてのスクリプトや入力が安全になるわけではありません。値を検証し、スクリプトが実行する処理も考慮してください。

  4. Bashスクリプトに引数を渡せますか?
    はい。subprocessの引数リストで、スクリプトのパスの後に各引数を個別の項目として追加します。os.systemはコマンド文字列を1つ受け取るため、同じように構造化して引数を渡す方法は提供しません。

  5. スクリプトに入力を渡すにはどうすればよいですか?
    単純な有限のやり取りにはsubprocess.runのinput=...を使います。より細かなプロセス制御が必要な場合は、Popenでstdin=subprocess.PIPEを設定し、communicate(input=...)を使います。communicate()は入力を送り、子プロセスの終了後にプロセスのストリームを回収します。