Python転職初心者向けエンジニアリングブログ

Pythonに魅了されたあなたへ。エンジニアリングの扉を開く転職初心者向けのブログへようこそ。このブログでは、Pythonの奥深さに迫りながら、エンジニアリングへの転職に役立つ情報を提供しています。未経験者から始めるPythonエンジニアリングの世界への一歩を踏み出すためのガイダンス、ベストプラクティス、そして成功事例など、初心者の方でもわかりやすいコンテンツをお届けします。

Pythonのsubprocessモジュールと`run`関数の詳細解説

LYPプレミアム会員 python

Pythonのsubprocessモジュールとrun関数の詳細解説

Pythonには外部コマンドを実行するためのいくつかの方法がありますが、その中でも標準的に利用されるのがsubprocessモジュールです。このモジュールを使うことで、Pythonスクリプトからシェルコマンドや他のプログラムを呼び出すことができます。具体的なユースケースとしては、シェルスクリプトを実行したり、外部プログラムに引数を渡して結果を受け取るなどの処理が挙げられます。

今回の記事では、subprocessモジュールのrun関数に焦点を当てて、その基本的な使い方やオプションについて詳しく解説します。具体例を交えながら、実際のコードとその出力結果を確認しつつ進めていきます。

subprocess.runとは

subprocess.runは、Python 3.5で導入されたsubprocessモジュール内の関数で、コマンドを実行し、その結果を取得するために使われます。それまで使われていたsubprocess.callsubprocess.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.runCompletedProcessオブジェクトを返します。このオブジェクトには、実行したコマンドの結果や終了ステータスなどが含まれています。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で外部コマンドを実行するための非常に強力で柔軟な関数です。今回の記事では、基本的な使い方から標準出力・エラーの取得、タイムアウトや環境変数の設定など、実践的な例を交えて詳しく解説しました。