PythonからOSコマンドを実行する方法
Pythonは、オペレーティングシステム(OS)のコマンドを直接実行するための強力な機能を提供しています。これにより、Pythonスクリプトからファイル操作、プロセス管理、ネットワーク設定など、OSレベルのタスクを自動化することが可能になります。この機能を利用するには、主にsubprocessモジュールが用いられます。
subprocessモジュール
subprocessモジュールは、新しいプロセスを生成し、それらの入出力パイプに接続し、そのリターンコードを取得するための機能を提供します。このモジュールは、従来のos.system()やos.popen*()関数よりも、より柔軟で強力なプロセス管理を可能にします。
subprocess.run()
subprocess.run()関数は、Python 3.5以降で導入された、コマンド実行のための推奨される方法です。この関数は、指定されたコマンドを実行し、コマンドの完了を待ち、その結果を返します。
subprocess.run()の基本的な使い方は以下の通りです:
import subprocess
# lsコマンドを実行する例
result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
# 標準出力の取得
print("標準出力:")
print(result.stdout)
# 標準エラー出力の取得
print("標準エラー出力:")
print(result.stderr)
# 終了コードの取得
print("終了コード:")
print(result.returncode)
subprocess.run()関数は、様々な引数を受け取ります。
args: 実行するコマンドとその引数をリスト形式で指定します。例えば、['ls', '-l']はls -lコマンドを表します。capture_output=True: 標準出力と標準エラー出力をキャプチャします。これにより、コマンドの実行結果をPythonスクリプト内で利用できるようになります。text=True(またはencoding='utf-8'): 標準出力と標準エラー出力をバイト列ではなく文字列として扱います。これは、テキストベースのコマンド結果を扱う場合に便利です。check=True: コマンドがゼロ以外の終了コードで終了した場合にCalledProcessError例外を発生させます。これにより、コマンドの失敗を早期に検知できます。shell=True: コマンドをシェル経由で実行します。これにより、シェル特有の機能(パイプ、リダイレクトなど)を利用できます。ただし、セキュリティ上のリスクがあるため、信頼できない入力をshell=Trueで実行する場合は注意が必要です。
subprocess.Popen()
subprocess.Popen()は、より低レベルなプロセス制御を提供します。これにより、非同期にコマンドを実行したり、複数のプロセス間でパイプを接続したりするなど、高度な操作が可能になります。
subprocess.Popen()の基本的な使い方は以下の通りです:
import subprocess
# 非同期でlsコマンドを実行する例
process = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
# コマンドの完了を待つ
stdout, stderr = process.communicate()
print("標準出力:")
print(stdout)
print("標準エラー出力:")
print(stderr)
print("終了コード:")
print(process.returncode)
subprocess.Popen()の主な引数:
args: 実行するコマンドとその引数。stdout=subprocess.PIPE: 標準出力をパイプに接続します。stderr=subprocess.PIPE: 標準エラー出力をパイプに接続します。stdin=subprocess.PIPE: 標準入力をパイプに接続します。shell=True: シェル経由で実行します。
Popenオブジェクトのcommunicate()メソッドは、プロセスの標準入力にデータを送信し、標準出力と標準エラー出力を読み取って、タプル(stdout, stderr)を返します。このメソッドは、プロセスが終了するまで待機します。
コマンド実行時の注意点
PythonからOSコマンドを実行する際には、いくつかの注意点があります。
セキュリティ
shell=Trueオプションを使用する場合、ユーザーからの入力など、信頼できないソースからのデータをコマンドに直接渡すと、コマンドインジェクション攻撃のリスクが生じます。可能な限りshell=False(デフォルト)を使用し、コマンドと引数をリスト形式で渡すようにしてください。
エラーハンドリング
コマンドの実行が失敗した場合、subprocess.run(..., check=True)を使用するか、result.returncodeをチェックすることで、エラーを適切に処理することが重要です。例外処理(try-exceptブロック)を適切に用いることで、スクリプトの堅牢性を高めることができます。
プラットフォーム依存性
実行するコマンドは、OSに依存します。例えば、Windowsではdirコマンド、Linux/macOSではlsコマンドとなります。クロスプラットフォームなスクリプトを作成する場合は、OSを判別し、適切なコマンドを実行するロジックを組み込む必要があります。
エンコーディング
コマンドの出力がテキストである場合、text=Trueやencoding引数を使用して、文字化けを防ぐことが重要です。システムのデフォルトエンコーディングとコマンドの出力エンコーディングが一致しない場合に問題が発生する可能性があります。
os.system() と `os.popen()`
subprocessモジュールの登場以前は、os.system()やos.popen*()関数がOSコマンド実行によく使われていました。これらは現在でも利用可能ですが、subprocessモジュールの方がより強力で柔軟な機能を提供するため、新規のコードではsubprocessの使用が推奨されています。
os.system()
os.system(command)は、指定されたcommandをサブシェルで実行し、コマンドの終了ステータスを返します。標準出力や標準エラー出力をキャプチャする機能はありません。
import os
# lsコマンドを実行する例
return_code = os.system('ls -l')
print(f"終了コード: {return_code}")
os.popen()
os.popen(command, mode='r')は、指定されたcommandを実行し、その標準出力または標準エラー出力をファイルオブジェクトとして返します。これにより、コマンドの出力を読み取ることができます。mode='w'を指定すると、コマンドに標準入力としてデータを書き込むことができます。
import os
# lsコマンドの出力を読み取る例
with os.popen('ls -l', 'r') as f:
output = f.read()
print(output)
まとめ
PythonからOSコマンドを実行することは、スクリプトの機能を拡張し、多くの自動化タスクを効率化するための強力な手段です。subprocessモジュール、特にsubprocess.run()関数は、コマンド実行における標準的な方法であり、その柔軟性と安全性から推奨されます。コマンド実行時には、セキュリティ、エラーハンドリング、プラットフォーム依存性、エンコーディングなどに十分注意を払うことで、安全で堅牢なPythonスクリプトを作成することができます。
