読み込み中...

Pythonのsubprocess.runでコマンドを実行する方法を10選

subprocess.runの徹底解説画像 Python
この記事は約37分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●subprocess.runとは?Pythonでコマンドを実行する魔法

Pythonを使っていると、システムコマンドを実行したい場面にしばしば遭遇しますよね。

そんな時に頼りになるのが、Pythonの標準ライブラリである「subprocess」モジュールです。

その中でも特に便利なのが「subprocess.run」関数。

この関数を使えば、Pythonからシステムコマンドを簡単に実行できちゃうんです。

コマンドの実行結果を取得したり、エラー処理を行ったりと、さまざまな操作が可能になります。

subprocess.runは、Pythonでコマンドを実行する際の強力な味方と言えるでしょう。

今回は、そんなsubprocess.runの魅力に迫っていきたいと思います!

○subprocess.runの基本的な使い方

では早速、subprocess.runの基本的な使い方を見ていきましょう。

subprocess.runを使ってコマンドを実行するには、次のようなコードを書きます。

import subprocess

result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
print(result.stdout)

このコードでは、ls -lコマンドを実行し、その結果をresult変数に格納しています。

capture_output=Trueを指定することで、コマンドの標準出力と標準エラー出力をキャプチャできます。

また、text=Trueを指定することで、キャプチャした出力を文字列として扱うことができます。

実行結果はresult.stdoutで取得できます。この例では、ls -lコマンドの結果が表示されるはずです。

コマンドの実行が失敗した場合は、subprocess.CalledProcessError例外が発生します。

この例外をキャッチすることで、エラー処理を行うことができますよ。

import subprocess

try:
    result = subprocess.run(["ls", "nonexistent_file"], capture_output=True, text=True, check=True)
except subprocess.CalledProcessError as e:
    print(f"コマンド実行エラー: {e}")
else:
    print(result.stdout)

この例では、存在しないファイルnonexistent_fileを指定してlsコマンドを実行しています。

check=Trueを指定することで、コマンドの実行が失敗した場合に例外が発生するようになります。

例外が発生した場合は、exceptブロックで例外をキャッチし、エラーメッセージを表示しています。

コマンドが正常に実行された場合は、elseブロックで結果を表示します。

このように、subprocess.runを使えば、Pythonからシステムコマンドを簡単に実行し、結果を取得したり、エラー処理を行ったりできます。

○subprocess.runとsubprocess.callの違い

ここで、少し蛇足かもしれませんが、subprocess.runとsubprocess.callの違いについても触れておきましょう。

実は、subprocess.runは比較的新しい関数で、Python 3.5から導入されました。

それ以前は、subprocess.callを使ってコマンドを実行するのが一般的でした。

subprocess.callとsubprocess.runの主な違いは、subprocess.runがコマンドの実行結果をCompletedProcessオブジェクトとして返すのに対し、subprocess.callは単にコマンドの終了コードを返すという点です。

また、subprocess.runではキャプチャした出力を文字列として扱うことができますが、subprocess.callでは出力を直接取得することができません。

ですから、基本的にはsubprocess.runを使うことをおすすめします。

subprocess.runの方が機能が豊富で、コードの可読性も高くなるでしょう。

とはいえ、subprocess.callも依然として使われているので、古いコードを読む際には知っておくと良いかもしれませんね。

●10個の実践的なsubprocess.run活用法

さて、subprocess.runの基本的な使い方はわかりましたが、実際の開発現場では、もっと様々な場面でsubprocess.runを活用することができます。

ここからは、subprocess.runを使った実践的な活用法を10個ほど紹介していきたいと思います。

コマンド実行の結果を取得したり、エラー処理を行ったり、タイムアウトを設定したりと、より実用的なテクニックが満載です。

それでは、早速1つ目のサンプルコードから見ていきましょう。

○サンプルコード1:シンプルなコマンド実行

まずは、シンプルなコマンド実行の例から始めましょう。

先ほども紹介したls -lコマンドを実行するコードです。

import subprocess

result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
print(result.stdout)

このコードを実行すると、カレントディレクトリの詳細なファイル一覧が表示されるはずです。

ls -lコマンドの出力結果が、result.stdoutに格納されているんですね。

実行結果の例

total 24
-rw-r--r-- 1 user group 1024 May 1 12:00 file1.txt
-rw-r--r-- 1 user group 2048 May 2 15:30 file2.txt
drwxr-xr-x 2 user group 4096 May 3 09:45 directory1

こんな感じで、subprocess.runを使えば、Pythonから簡単にシステムコマンドを実行し、その結果を取得することができます。

○サンプルコード2:戻り値の取得と活用

次に、コマンドの戻り値を取得し、それを活用する方法を見ていきましょう。

例えば、pingコマンドを実行し、その戻り値によって処理を分岐させるようなコードを書いてみます。

import subprocess

host = "www.example.com"
result = subprocess.run(["ping", "-c", "1", host], capture_output=True)

if result.returncode == 0:
    print(f"{host} is reachable!")
else:
    print(f"{host} is not reachable.")

この例では、pingコマンドを使って指定したホストに対して疎通確認を行っています。

-c 1オプションを付けることで、1回だけpingを送信するようにしています。

コマンドの実行結果はresult.returncodeに格納されます。

この値が0であれば、pingが成功したことを意味するので、”reachable”というメッセージを表示します。

0以外の場合は、pingが失敗したことを意味するので、”not reachable”というメッセージを表示するようにしています。

実行結果の例(pingが成功した場合)

www.example.com is reachable!

実行結果の例(pingが失敗した場合)

www.example.com is not reachable.

このように、subprocess.runの戻り値を活用することで、コマンドの実行結果に応じて処理を切り替えることができます。

○サンプルコード3:標準出力を取得する方法

続いて、コマンドの標準出力を取得する方法について見ていきましょう。

先ほどのls -lコマンドの例でも少し触れましたが、ここではもう少し詳しく説明します。

標準出力を取得するには、subprocess.runcapture_outputパラメータをTrueに設定し、stdout属性でアクセスします。

import subprocess

result = subprocess.run(["echo", "Hello, World!"], capture_output=True, text=True)
print(result.stdout)

この例では、echoコマンドを使って”Hello, World!”という文字列を標準出力に出力しています。

capture_output=Trueを指定することで、その出力をresult.stdoutに格納しています。

また、text=Trueを指定することで、出力を文字列として扱うようにしています。

実行結果

Hello, World!

標準出力に出力された”Hello, World!”という文字列が、そのままresult.stdoutに格納され、表示されていますね。

capture_output=Trueを指定しない場合、標準出力は直接コンソールに出力されてしまい、result.stdoutでは取得できないので注意が必要です。

○サンプルコード4:エラー処理とexception

subprocess.runを使ってコマンドを実行する際、エラーが発生することがあります。

例えば、存在しないコマンドを実行しようとしたり、コマンドの権限がない場合などです。

そんな時は、適切にエラー処理を行うことが大切ですよね。

ここでは、subprocess.runでのエラー処理の方法を見ていきましょう。

エラーが発生した場合、subprocess.runはsubprocess.CalledProcessErrorという例外を発生させます。

この例外をキャッチすることで、エラー処理を行うことができます。

import subprocess

try:
    result = subprocess.run(["ls", "nonexistent_file"], capture_output=True, text=True, check=True)
except subprocess.CalledProcessError as e:
    print(f"コマンド実行エラー: {e}")
    print(f"returncode: {e.returncode}")
    print(f"output: {e.output}")
else:
    print(result.stdout)

この例では、存在しないファイルnonexistent_fileに対してlsコマンドを実行しています。

check=Trueを指定することで、コマンドの実行が失敗した場合に例外が発生するようにしています。

例外が発生した場合は、exceptブロックでキャッチし、エラーメッセージや戻り値、出力内容を表示しています。

コマンドが正常に実行された場合は、elseブロックで結果を表示します。

実行結果

コマンド実行エラー: Command '['ls', 'nonexistent_file']' returned non-zero exit status 1.
returncode: 1
output: ls: nonexistent_file: No such file or directory

エラーメッセージには、実行したコマンドや戻り値、エラー内容が含まれています。

e.returncodeで戻り値を、e.outputで出力内容を取得することができます。

エラー処理を適切に行うことで、コマンド実行時のトラブルにも柔軟に対応できるようになります。

ただし、check=Trueを指定しない場合、例外は発生しません。

その場合は、result.returncodeをチェックすることで、コマンドの実行状態を確認する必要があります。

○サンプルコード5:タイムアウトの設定

コマンドの実行には時間がかかることがあります。

特に、ネットワーク関連のコマンドや、重たい処理を行うコマンドなどは、レスポンスが返ってくるまでに時間がかかってしまうことも。

そんな時は、subprocess.runにタイムアウトを設定することで、一定時間経過後に処理を中断させることができます。

import subprocess

try:
    result = subprocess.run(["ping", "-c", "1", "8.8.8.8"], capture_output=True, text=True, timeout=5)
    print(result.stdout)
except subprocess.TimeoutExpired as e:
    print("タイムアウトしました")

この例では、Googleの公開DNS(8.8.8.8)に対してpingコマンドを実行しています。

timeout=5を指定することで、5秒経過するとタイムアウトするようにしています。

タイムアウトすると、subprocess.TimeoutExpired例外が発生します。

この例外をキャッチすることで、タイムアウト時の処理を記述することができます。

実行結果(タイムアウトした場合)

タイムアウトしました

ネットワークの状態によっては、pingが5秒以内に返ってこない場合があります。

そのような場合に、無限に待ち続けるのではなく、一定時間でタイムアウトさせることができるのは便利ですよね。

タイムアウト時間は、コマンドの特性や環境に合わせて適切に設定することが大切です。

○サンプルコード6:環境変数の指定

コマンドを実行する際に、環境変数を指定したい場合があります。

例えば、コマンドの動作を変更したり、APIキーなどの秘密情報を渡したりする場合などです。

subprocess.runでは、envパラメータを使って環境変数を指定することができます。

import subprocess

env = {
    "API_KEY": "abc123",
    "DEBUG": "1"
}

result = subprocess.run(["python", "script.py"], capture_output=True, text=True, env=env)
print(result.stdout)

この例では、script.pyというPythonスクリプトを実行する際に、API_KEYDEBUGという環境変数を設定しています。

envパラメータには、環境変数名と値をキーと値に持つ辞書を指定します。

この辞書に指定された環境変数が、コマンド実行時の環境変数としてセットされます。

script.pyの中では、os.environを使ってこの環境変数にアクセスすることができます。

# script.py
import os

api_key = os.environ.get("API_KEY")
debug_mode = os.environ.get("DEBUG")

print(f"API_KEY: {api_key}")
print(f"DEBUG_MODE: {debug_mode}")

実行結果

API_KEY: abc123
DEBUG_MODE: 1

環境変数を指定することで、コマンドの動作を柔軟に制御することができます。

APIキーなどの秘密情報を環境変数で渡すことで、コードに直接記述するよりも安全に管理することができますね。

ただし、環境変数には機密情報を含めないように注意が必要です。

コマンドの実行ログなどに環境変数の内容が残ってしまう可能性があるからです。

○サンプルコード7:ディレクトリの変更

コマンドを実行する際に、ワーキングディレクトリを変更したい場合があります。

例えば、特定のディレクトリ下でコマンドを実行したい場合などです。

subprocess.runでは、cwdパラメータを使ってワーキングディレクトリを指定することができます。

import subprocess

result = subprocess.run(["ls", "-l"], capture_output=True, text=True, cwd="/path/to/directory")
print(result.stdout)

この例では、/path/to/directoryディレクトリに移動した状態で、ls -lコマンドを実行しています。

cwdパラメータに、移動先のディレクトリパスを指定します。

実行結果

total 0
-rw-r--r-- 1 user group 0 Jun 1 12:00 file1.txt
-rw-r--r-- 1 user group 0 Jun 1 12:00 file2.txt

指定したディレクトリ下でコマンドが実行され、そのディレクトリ内のファイル一覧が表示されます。

ワーキングディレクトリを変更することで、特定のディレクトリ下でコマンドを実行したり、相対パスでファイルを指定したりすることができるようになります。

ただし、指定したディレクトリが存在しない場合は、FileNotFoundError例外が発生するので注意が必要です。

○サンプルコード8:シェルコマンドの実行

PythonのSubprocess.runを使えば、シェルコマンドも簡単に実行できちゃいます。

でも、ちょっと注意が必要なんです。

シェルコマンドを実行する際は、shell=Trueオプションを指定する必要があります。

でも、そうすると、シェルインジェクションの脆弱性が発生する可能性があるんですよね。

import subprocess

# 危険な例(シェルインジェクションの脆弱性あり)
command = "ls -l ; rm -rf /tmp/important_dir"
result = subprocess.run(command, shell=True, capture_output=True, text=True)
print(result.stdout)

こんな感じでコマンドを指定すると、ls -lだけでなく、rm -rf /tmp/important_dirも実行されてしまいます。

大切なディレクトリが消えちゃうかもしれません。

怖いですよね。

ですから、シェルコマンドを実行する際は、ユーザーからの入力値を直接コマンドに渡すのは避けるべきです。

代わりに、コマンドとその引数をリストで指定するのがおすすめです。

import subprocess

# 安全な例(コマンドと引数をリストで指定)
command = ["ls", "-l", "/tmp"]
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout)

リストでコマンドと引数を指定することで、シェルインジェクションのリスクを減らすことができます。

でも、どうしてもシェルの機能を使いたい場合もありますよね。

そんな時は、シェルコマンドをリストの要素として渡すことで、安全にシェルコマンドを実行することができます。

import subprocess

# シェルコマンドをリストで指定
command = ["sh", "-c", "ls -l | grep '.txt'"]
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout)

こんな感じで、sh -cを使ってシェルコマンドを渡すことで、パイプやリダイレクトなどのシェルの機能を安全に使うことができるんです。

でも、やっぱりシェルコマンドを実行する際は、十分な注意が必要ですね。

システムに悪影響を与えるコマンドを実行しないように気をつけましょう。

○サンプルコード9:入力のリダイレクト

Subprocess.runでは、コマンドへの入力をリダイレクトすることもできます。

つまり、コマンドにデータを渡すことができるんです。

例えば、catコマンドを使って、文字列を標準入力から渡してみましょう。

import subprocess

input_data = "Hello, world!"
result = subprocess.run(["cat"], input=input_data, capture_output=True, text=True)
print(result.stdout)

この例では、input_dataに格納された文字列をcatコマンドの標準入力にリダイレクトしています。

inputパラメータに文字列やbytesオブジェクトを指定することで、コマンドにデータを渡すことができます。

実行結果

Hello, world!

input_dataの内容がそのままcatコマンドに渡され、標準出力に出力されているのがわかりますね。

入力のリダイレクトは、コマンドにデータを渡す必要がある場合に便利です。例えば、テキストファイルの内容を処理するコマンドにデータを渡したい場合などに活用できます。

import subprocess

with open("input.txt", "r") as file:
    input_data = file.read()

result = subprocess.run(["sed", "s/hello/Hello/g"], input=input_data, capture_output=True, text=True)
print(result.stdout)

この例では、input.txtファイルの内容を読み込み、sedコマンドに渡しています。

sedコマンドは、入力データ内の”hello”という文字列を”Hello”に置換しています。

実行結果

Hello, world!
Hello, Python!

ファイルの内容がsedコマンドによって処理され、置換後の結果が標準出力に出力されました。

入力のリダイレクトを使うことで、ファイルやデータをコマンドに渡し、処理することができます。

データの前処理やフィルタリングなどに活用できそうですね。

○サンプルコード10:非同期実行とバックグラウンド処理

Subprocess.runは、デフォルトではコマンドの実行が完了するまでブロックします。

つまり、コマンドの実行が終わるまで、次の処理に進まないんです。

でも、コマンドの実行に時間がかかる場合や、並行して他の処理を進めたい場合もありますよね。

そんな時は、コマンドを非同期に実行したり、バックグラウンドで処理したりすることができます。

まずは、コマンドを非同期に実行する方法から見ていきましょう。

import subprocess

process = subprocess.Popen(["sleep", "5"])
print("コマンドを実行しました")

process.wait()
print("コマンドが完了しました")

この例では、sleepコマンドを使って5秒間の待機を行っています。

subprocess.Popenを使ってコマンドを実行すると、すぐに次の処理に進みます。

実行結果:

コマンドを実行しました
(5秒後に次の行が表示される)
コマンドが完了しました

subprocess.Popenを使うことで、コマンドを非同期に実行できるんです。

print文が先に実行され、その後にsleepコマンドが完了するまで待機しています。

process.wait()を呼び出すことで、コマンドの完了を待つことができます。

非同期に実行されたコマンドの完了を確認したい場合に便利ですね。

次に、コマンドをバックグラウンドで実行する方法を見ていきましょう。

import subprocess

process = subprocess.Popen(["sleep", "5"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print("コマンドをバックグラウンドで実行しました")

# 他の処理を実行する
# ...

process.poll()
if process.returncode is None:
    print("コマンドはまだ実行中です")
else:
    print("コマンドが完了しました")

この例では、sleepコマンドをバックグラウンドで実行しています。

stdoutstderrsubprocess.DEVNULLを指定することで、コマンドの出力を抑制しています。

subprocess.Popenを使ってコマンドを実行した後、他の処理を実行することができます。

バックグラウンドで実行されているコマンドは、独立して処理を進めます。

process.poll()を呼び出すことで、コマンドの実行状態を確認できます。

process.returncodeがNoneの場合は、コマンドがまだ実行中であることを表します。

Noneでない場合は、コマンドが完了したことを示し、その値が終了コードになります。

●subprocess.runのパワーアップテクニック

さて、subprocess.runの基本的な使い方はマスターできましたね。

でも、もっと効率的に、もっと便利にsubprocess.runを使いたいと思いませんか?

ここからは、subprocess.runをさらにパワーアップさせるテクニックをご紹介します。

リアルタイムで標準出力を取得したり、複数のコマンドを連続して実行したりと、より実践的なテクニックが満載ですよ!

それでは、早速1つ目のテクニックから見ていきましょう。

○リアルタイムで標準出力を取得する方法

subprocess.runを使ってコマンドを実行すると、通常はコマンドの実行が完了するまで、標準出力を取得することができません。

でも、コマンドの実行中にリアルタイムで標準出力を取得したい場合もありますよね。

そんな時は、subprocess.Popenを使って、コマンドの標準出力をリアルタイムで取得することができます。

import subprocess

process = subprocess.Popen(["ping", "-c", "5", "google.com"], stdout=subprocess.PIPE, universal_newlines=True)

for line in process.stdout:
    print(line, end="")

この例では、pingコマンドを使ってGoogleへの疎通確認を行っています。

subprocess.Popenを使ってコマンドを実行し、stdout=subprocess.PIPEを指定することで、標準出力をパイプで取得できるようにしています。

universal_newlines=Trueを指定することで、標準出力を文字列として扱うことができます。

実行結果

PING google.com (172.217.31.142) 56(84) bytes of data.
64 bytes from nrt12s18-in-f14.1e100.net (172.217.31.142): icmp_seq=1 ttl=54 time=31.8 ms
64 bytes from nrt12s18-in-f14.1e100.net (172.217.31.142): icmp_seq=2 ttl=54 time=37.1 ms
64 bytes from nrt12s18-in-f14.1e100.net (172.217.31.142): icmp_seq=3 ttl=54 time=32.0 ms
64 bytes from nrt12s18-in-f14.1e100.net (172.217.31.142): icmp_seq=4 ttl=54 time=39.7 ms
64 bytes from nrt12s18-in-f14.1e100.net (172.217.31.142): icmp_seq=5 ttl=54 time=31.2 ms

--- google.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 31.236/34.419/39.748/3.477 ms

process.stdoutをイテレートすることで、コマンドの実行中に標準出力をリアルタイムで取得することができます。

pingコマンドの出力が1行ずつ表示されているのがわかりますね。

リアルタイムで標準出力を取得できると、コマンドの進行状況を監視したり、途中経過を表示したりすることができます。

ログの出力をリアルタイムで確認したい場合などにも便利ですよ。

○複数のコマンドを連続して実行する

次に、複数のコマンドを連続して実行する方法を見ていきましょう。

タスクの自動化などで、複数のコマンドを順番に実行したい場合がありますよね。

subprocess.runを使えば、複数のコマンドを簡単に連続して実行することができます。

import subprocess

commands = [
    ["echo", "コマンド1を実行します"],
    ["sleep", "2"],
    ["echo", "コマンド2を実行します"],
    ["sleep", "2"],
    ["echo", "コマンド3を実行します"]
]

for command in commands:
    result = subprocess.run(command, capture_output=True, text=True)
    print(result.stdout)

この例では、commandsリストに複数のコマンドを定義しています。

各コマンドは、コマンド名と引数のリストで表現されています。

forループを使って、commandsリストの各コマンドを順番に実行しています。

subprocess.runを使ってコマンドを実行し、標準出力を取得して表示しています。

実行結果

コマンド1を実行します

コマンド2を実行します

コマンド3を実行します

各コマンドが順番に実行され、その出力が表示されました。

sleepコマンドを使って、コマンドの実行間に一定の間隔を設けています。

複数のコマンドを連続して実行することで、一連の処理を自動化することができます。

例えば、ファイルのバックアップやデータの前処理など、決まった手順で複数のコマンドを実行する場合に便利ですね。

○サブプロセスの終了を確認する方法

最後に、サブプロセスの終了を確認する方法を見ていきましょう。

コマンドを実行した後、そのコマンドが正常に終了したかどうかを確認したい場合がありますよね。

subprocess.runを使えば、コマンドの終了コードを取得することで、サブプロセスの終了状態を確認することができます。

import subprocess

result = subprocess.run(["ls", "nonexistent_file"], capture_output=True, text=True)

if result.returncode == 0:
    print("コマンドは正常に終了しました")
else:
    print(f"コマンドは異常終了しました(終了コード: {result.returncode})")
    print(f"標準エラー出力: {result.stderr}")

この例では、存在しないファイルnonexistent_fileに対してlsコマンドを実行しています。

result.returncodeを確認することで、コマンドの終了コードを取得することができます。終了コードが0の場合は、コマンドが正常に終了したことを表します。

0以外の場合は、コマンドが異常終了したことを表します。

異常終了した場合は、result.stderrを使って標準エラー出力を取得し、表示することができます。

実行結果:

コマンドは異常終了しました(終了コード: 1)
標準エラー出力: ls: nonexistent_file: No such file or directory

lsコマンドが存在しないファイルに対して実行されたため、異常終了しています。

終了コードが1であることを示し、標準エラー出力にエラーメッセージが表示されました。

サブプロセスの終了状態を確認することで、コマンドの実行結果に応じて適切な処理を行うことができます。

エラーが発生した場合にログを出力したり、リトライ処理を行ったりするのに役立ちますね。

●よくあるエラーと対処法

subprocess.runを使ってコマンドを実行していると、時々エラーに遭遇することがありますよね。

でも、どんなエラーが発生するのか、そしてその対処法を知っておくことで、トラブルに適切に対応できるようになります。

ここでは、subprocess.runを使う際によく遭遇するエラーとその解決策を3つご紹介します。

エラーメッセージを見ただけでは何が原因なのかわからないこともありますが、落ち着いて原因を突き止めていきましょう。

それでは、早速1つ目のエラーから見ていきましょう。

○「FileNotFoundError」の解決策

subprocess.runでコマンドを実行しようとしたら、突然「FileNotFoundError」が発生して困ってしまった経験はありませんか?

「FileNotFoundError」は、指定したコマンドが見つからない場合に発生するエラーです。

つまり、実行しようとしたコマンドが存在しないか、パスが正しく指定されていない可能性があります。

import subprocess

try:
    result = subprocess.run(["nonexistent_command"], capture_output=True, text=True, check=True)
except FileNotFoundError as e:
    print(f"コマンドが見つかりませんでした: {e}")

この例では、存在しないコマンドnonexistent_commandを実行しようとしています。

check=Trueを指定しているため、コマンドが見つからない場合にFileNotFoundErrorが発生します。

実行結果

コマンドが見つかりませんでした: [Errno 2] No such file or directory: 'nonexistent_command'

エラーメッセージを見ると、nonexistent_commandというファイルやディレクトリが存在しないことがわかります。

「FileNotFoundError」が発生した場合は、次の点を確認してみましょう。

  1. コマンドの名前が正しいか確認する
  2. コマンドのパスが通っているか確認する
  3. コマンドが実行可能な状態になっているか確認する

コマンドの名前やパスを正しく指定することで、「FileNotFoundError」を解決することができます。

○「PermissionError」への対応

次に、「PermissionError」について見ていきましょう。

「PermissionError」は、コマンドを実行する権限がない場合に発生するエラーです。

import subprocess

try:
    result = subprocess.run(["sudo", "ls", "/root"], capture_output=True, text=True, check=True)
except PermissionError as e:
    print(f"権限がありません: {e}")

この例では、sudoコマンドを使って/rootディレクトリの内容を表示しようとしていますが、適切な権限がない場合に「PermissionError」が発生します。

実行結果

権限がありません: [Errno 13] Permission denied

エラーメッセージを見ると、権限が拒否されたことがわかります。

「PermissionError」が発生した場合は、次の点を確認してみましょう。

  1. 適切な権限でコマンドを実行しているか確認する
  2. 必要な権限を付与する(sudoなどを使用する)
  3. セキュリティ上の理由から、権限を付与できない場合は別の方法を検討する

適切な権限でコマンドを実行することで、「PermissionError」を解決することができます。

ただし、セキュリティ上の観点から、不必要な権限の付与は避けるべきです。

○文字化けトラブルの解消法

最後に、文字化けトラブルの解消法について見ていきましょう。

コマンドの実行結果が文字化けしてしまい、正しく表示されないことがありますよね。

文字化けの原因は、主に文字エンコーディングの違いによるものです。

Pythonとコマンドの出力で使用されている文字エンコーディングが一致していないと、文字化けが発生してしまいます。

import subprocess

result = subprocess.run(["ls", "-l"], capture_output=True)
print(result.stdout)

この例では、ls -lコマンドを実行し、その出力を表示しています。

しかし、出力が文字化けしてしまうことがあります。

実行結果(文字化けの例)

b'total 0\n-rw-r--r-- 1 user group 0 May 1 12:00 \xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab1.txt\n-rw-r--r-- 1 user group 0 May 1 12:00 \xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab2.txt\n'

文字化けを解消するには、次の方法を試してみましょう。

  1. text=Trueを指定してコマンドを実行する
  2. encodingパラメータを指定して、適切な文字エンコーディングを設定する
  3. 出力をデコードする
import subprocess

result = subprocess.run(["ls", "-l"], capture_output=True, text=True, encoding="utf-8")
print(result.stdout)

text=Trueを指定することで、出力を文字列として扱うことができます。

さらに、encodingパラメータを使って適切な文字エンコーディングを指定します。

実行結果(文字化け解消後)

total 0
-rw-r--r-- 1 user group 0 May 1 12:00 ファイル1.txt
-rw-r--r-- 1 user group 0 May 1 12:00 ファイル2.txt

文字エンコーディングを適切に設定することで、文字化けを解消することができました。

文字化けトラブルに遭遇した場合は、文字エンコーディングを意識して対処することが大切です。

適切な文字エンコーディングを指定することで、コマンドの出力を正しく扱うことができます。

●subprocess.runの応用例

さて、ここまでsubprocess.runの基本的な使い方やエラー対処法について解説してきましたが、実際の開発現場ではどのようにsubprocess.runを活用できるのでしょうか?

ここからは、subprocess.runを使った実践的な応用例を4つご紹介します。

システム情報の取得からファイル操作の自動化、ネットワーク診断ツールの作成まで、様々なシーンでsubprocess.runが活躍します。

それでは、具体的なサンプルコードを交えながら、subprocess.runの応用例を見ていきましょう。

○サンプルコード11:システム情報の取得

システム管理のタスクでは、OSやハードウェアの情報を取得する必要があることが多いですよね。

subprocess.runを使えば、システム情報を簡単に取得することができます。

例えば、Linuxシステムでメモリ使用量を取得してみましょう。

import subprocess

command = ["free", "-h"]
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout)

この例では、freeコマンドを使ってメモリ使用量を取得しています。

-hオプションを指定することで、人間が読みやすい形式で出力されます。

実行結果

              total        used        free      shared  buff/cache   available
Mem:          7.7Gi       3.1Gi       2.0Gi       1.0Gi       2.6Gi       3.3Gi
Swap:         2.0Gi       0.0Ki       2.0Gi

メモリの総容量や使用量、空き容量などの情報が表示されました。

システム監視ツールやリソース管理ツールを作成する際に、subprocess.runを使ってシステム情報を取得することができます。

他にも、unameコマンドを使ってOSの情報を取得したり、lscpuコマンドを使ってCPUの情報を取得したりと、様々なシステム情報を取得することができますよ。

○サンプルコード12:ファイル操作の自動化

ファイル操作の自動化も、subprocess.runの得意分野の1つです。

ファイルのコピーや移動、圧縮や解凍など、様々なファイル操作をPythonから実行することができます。

例えば、tarコマンドを使ってファイルを圧縮してみましょう。

import subprocess

command = ["tar", "-czvf", "archive.tar.gz", "directory/"]
result = subprocess.run(command, capture_output=True, text=True)
print(result.stdout)

この例では、tarコマンドを使ってdirectoryディレクトリをarchive.tar.gzという名前で圧縮しています。

-czvfオプションは、圧縮(c)、gzip形式(z)、詳細出力(v)、ファイル名指定(f)を意味します。

実行結果

directory/
directory/file1.txt
directory/file2.txt
directory/subdirectory/
directory/subdirectory/file3.txt

圧縮処理の詳細出力が表示され、archive.tar.gzファイルが作成されました。

subprocess.runを使えば、バックアップの自動化やファイルの一括処理などを簡単に実装することができます。

他にも、cpコマンドを使ってファイルをコピーしたり、mvコマンドを使ってファイルを移動したりと、様々なファイル操作を自動化することができますよ。

○サンプルコード13:ネットワーク診断ツールの作成

ネットワーク関連のタスクでも、subprocess.runが活躍します。

ネットワーク接続の確認やトラブルシューティングに役立つツールを作成することができます。

例えば、pingコマンドを使って指定したホストに対して疎通確認を行うツールを作ってみましょう。

import subprocess

host = "www.example.com"
command = ["ping", "-c", "5", host]
result = subprocess.run(command, capture_output=True, text=True)

print(f"--- {host} ping statistics ---")
print(result.stdout)

この例では、pingコマンドを使ってwww.example.comに対して5回の疎通確認を行っています。

-cオプションで送信回数を指定しています。

実行結果

--- www.example.com ping statistics ---
PING www.example.com (93.184.216.34) 56(84) bytes of data.
64 bytes from 93.184.216.34 (93.184.216.34): icmp_seq=1 ttl=54 time=24.5 ms
64 bytes from 93.184.216.34 (93.184.216.34): icmp_seq=2 ttl=54 time=23.9 ms
64 bytes from 93.184.216.34 (93.184.216.34): icmp_seq=3 ttl=54 time=23.9 ms
64 bytes from 93.184.216.34 (93.184.216.34): icmp_seq=4 ttl=54 time=23.9 ms
64 bytes from 93.184.216.34 (93.184.216.34): icmp_seq=5 ttl=54 time=23.8 ms

--- www.example.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 23.805/24.023/24.541/0.277 ms

pingコマンドの実行結果が表示され、指定したホストへの疎通が確認できました。

ネットワーク診断ツールを作成する際に、subprocess.runを使って様々なネットワークコマンドを実行することができます。

他にも、tracerouteコマンドを使って経路情報を取得したり、nslookupコマンドを使ってDNS情報を取得したりと、ネットワーク関連のタスクを自動化することができますよ。

○サンプルコード14:外部APIとの連携

最後に、外部APIとの連携について見ていきましょう。

subprocess.runを使って、コマンドラインツールを介して外部APIと連携することができます。

例えば、curlコマンドを使ってWebサービスのAPIを呼び出してみましょう。

import subprocess
import json

api_url = "https://api.example.com/data"
command = ["curl", "-s", api_url]
result = subprocess.run(command, capture_output=True, text=True)

data = json.loads(result.stdout)
print(data)

この例では、curlコマンドを使ってhttps://api.example.com/dataというURLに対してGETリクエストを送信しています。

-sオプションでサイレントモードを指定し、出力をキャプチャしています。

APIから返されたJSONデータは、result.stdoutに格納されています。

json.loads()を使ってJSONデータをPythonのデータ構造に変換し、表示しています。

実行結果

{'id': 1, 'name': 'John Doe', 'email': 'john@example.com'}

APIから取得したデータが表示されました。

subprocess.runを使って外部コマンドを実行し、その出力結果を解析することで、外部APIとの連携を実現することができます。

他にも、wgetコマンドを使ってファイルをダウンロードしたり、ftpコマンドを使ってFTPサーバーとファイル転送したりと、様々な外部サービスとの連携が可能です。

まとめ

さて、ここまでPythonのsubprocess.runについて、基本的な使い方から実践的な活用法まで、幅広く 解説してきました。

今回学んだ知識を生かして、実際の開発プロジェクトでsubprocess.runを活用してみてください。

きっと、Pythonでのシステムコマンド実行が身近なものになるはずです。