### Pythonのsubprocessモジュールと`run`関数の詳細解説
Pythonには外部コマンドを実行するためのいくつかの方法がありますが、その中でも標準的に利用されるのがsubprocessモジュールです。このモジュールを使うことで、Pythonスクリプトからシェルコマンドや他のプログラムを呼び出すことができます。具体的なユースケースとしては、シェルスクリプトを実行したり、外部プログラムに引数を渡して結果を受け取るなどの処理が挙げられます。
今回の記事では、subprocessモジュールのrun関数に焦点を当てて、その基本的な使い方やオプションについて詳しく解説します。具体例を交えながら、実際のコードとその出力結果を確認しつつ進めていきます。
subprocess.runとは
subprocess.runは、Python 3.5で導入されたsubprocessモジュール内の関数で、コマンドを実行し、その結果を取得するために使われます。それまで使われていたsubprocess.callやsubprocess.check_outputよりも柔軟で扱いやすくなっており、現代的なPythonのコードでは一般的にこのrun関数を使います。
subprocess.runの基本的なシグネチャは以下の通りです。
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None, text=None, env=None, universal_newlines=None)
これだけ見るとかなり多くの引数を取ることが分かりますが、まずは基本的な使い方を抑えることが大切です。次に、それぞれの引数について説明していきます。
基本的な使い方
subprocess.runを使う際、最もシンプルな形は引数に実行したいコマンドをリスト形式で渡すことです。例えば、シェルコマンドでls -l(ディレクトリの内容をリスト表示)を実行する場合、以下のように書きます。
import subprocess result = subprocess.run(["ls", "-l"])
この場合、subprocess.runはCompletedProcessオブジェクトを返します。このオブジェクトには、実行したコマンドの結果や終了ステータスなどが含まれています。CompletedProcessの主な属性は次の通りです。
args: 実行されたコマンドreturncode: コマンドの終了コード(0は正常終了、それ以外は異常終了)stdout: コマンドの標準出力stderr: コマンドの標準エラー出力
それでは、このコードを実行した際の結果を見てみましょう。
実行結果
total 8 -rw-r--r-- 1 user staff 2048 Sep 28 14:25 example.txt -rw-r--r-- 1 user staff 1024 Sep 28 14:26 another_file.txt
このコードでは、シェルコマンドls -lが実行され、指定されたディレクトリ内のファイル一覧が表示されます。
コマンドの標準出力を取得する
次に、実行したコマンドの結果(標準出力)をPython内で扱う方法を紹介します。これには、stdout=subprocess.PIPEを使います。これにより、標準出力をパイプとして取得し、結果をCompletedProcessオブジェクトのstdout属性から取得できるようになります。
以下の例では、ls -lの出力をPythonの変数として取得します。
import subprocess result = subprocess.run(["ls", "-l"], stdout=subprocess.PIPE, text=True) print(result.stdout)
実行結果
total 8 -rw-r--r-- 1 user staff 2048 Sep 28 14:25 example.txt -rw-r--r-- 1 user staff 1024 Sep 28 14:26 another_file.txt
このように、result.stdoutにコマンドの結果が文字列として格納されます。ここでtext=Trueを指定している点に注意してください。これにより、バイトデータではなく文字列として結果が返されます。
エラー出力を取得する
コマンドが標準エラー出力にエラーメッセージを出力した場合、それも同様に取得することができます。これにはstderr=subprocess.PIPEを使います。
次の例では、存在しないコマンドを実行してエラーメッセージを取得します。
import subprocess result = subprocess.run(["ls", "non_existing_file"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) print("stdout:", result.stdout) print("stderr:", result.stderr)
実行結果
stdout: stderr: ls: non_existing_file: No such file or directory
このように、標準出力と標準エラー出力をそれぞれ個別に取得することができます。
コマンドの終了コードを確認する
実行したコマンドが正常に終了したかどうかは、CompletedProcessオブジェクトのreturncode属性で確認できます。終了コードが0であれば正常終了、それ以外の値であれば異常終了を意味します。
以下のコードでは、コマンドの終了コードをチェックし、異常終了した場合にエラーメッセージを出力する例を示します。
import subprocess result = subprocess.run(["ls", "non_existing_file"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) if result.returncode != 0: print(f"Error: {result.stderr}")
実行結果
Error: ls: non_existing_file: No such file or directory
この例では、lsコマンドが失敗しているため、エラーメッセージが表示されます。
check=Trueによるエラーハンドリング
コマンドが異常終了した場合に自動的に例外を発生させたい場合、run関数にcheck=Trueを指定します。このオプションを使うと、終了コードが0でない場合にsubprocess.CalledProcessErrorが発生します。
以下の例では、存在しないファイルを指定してエラーを強制的に発生させ、そのエラーハンドリングを行います。
import subprocess try: subprocess.run(["ls", "non_existing_file"], check=True) except subprocess.CalledProcessError as e: print(f"Command failed with return code {e.returncode}")
実行結果
Command failed with return code 2
このように、check=Trueを指定することで、異常終了時に明示的にエラーとして処理することが可能になります。
シェルコマンドの実行 (shell=True)
通常、subprocess.runでコマンドを実行する場合、コマンドはリスト形式で渡します。しかし、シェル固有の機能(例えば、パイプやリダイレクトなど)を使いたい場合は、shell=Trueを指定する必要があります。
以下の例では、シェルの機能を使って、echoコマンドの出力をgrepでフィルタリングしています。
import subprocess result = subprocess.run("echo 'hello world' | grep hello", shell=True, stdout=subprocess.PIPE, text=True) print(result.stdout)
実行結果
hello world
ここでshell=Trueを使わなければ、パイプを含むコマンドは正しく実行されません。シェルコマンドを直接実行する必要がある場合にのみ、このオプションを使います。
タイムアウトの設定
subprocess.runにはtimeoutオプションもあります。これを使うことで、コマンドが指定した時間内に終了しなかった場合にTimeoutExpired例外を発生させることができます。
次の例では、sleepコマンドで2秒待機しようとしますが、タイムアウトを1秒に設定しているため、例外が発生します。
import subprocess try: subprocess.run(["sleep", "2"], timeout=1) except subprocess.TimeoutExpired: print("The command timed out")
実行結果
The command timed out
環境変数の設定 (env)
subprocess.runでは、実行するコマンドに対してカスタムの環境変数を渡すことも可能です。これには、env引数を使います。例えば、以下のコードでは、新しい環境変数MY_VARを設定して、printenvコマンドでその値を確認しています。
import subprocess import os env = os.environ.copy() env["MY_VAR"] = "Hello" result = subprocess.run(["printenv", "MY_VAR"], stdout=subprocess.PIPE, env=env, text=True) print(result.stdout)
実行結果
Hello
まとめ
subprocess.runは、Pythonで外部コマンドを実行するための非常に強力で柔軟な関数です。今回の記事では、基本的な使い方から標準出力・エラーの取得、タイムアウトや環境変数の設定など、実践的な例を交えて詳しく解説しました。