Pythonのsubprocessモジュールの使い方|子プロセスの起動方法からわかりやすく解説
- システム
エンジニア - Pythonのsubprocessモジュールの使い方について教えていただけますか。
- プロジェクト
マネージャー - Pythonのsubprocessモジュールの使い方や子プロセスの起動方法、バックグラウンドでの実行方法、子プロセスだけでなく孫プロセスまでkillする方法などについて、Windows10を使用して解説いたしましょう。
Pythonのsubprocessとは?
Pythonのsubprocessとは一般的には子プロセス(child process)と呼ばれるプロセスを起動したりするためのモジュールです。
ここでは新しく用意された関数等を用いて解説しますのでPython3.5以降のできるだけ新しいバージョンで実行してください。以降Windows10と、その上で走るPython3.9.1を使って解説します。
コマンドを入力して子プロセスを起動する
Pythonではsubprocessモジュールを利用することで比較的簡単に子プロセスを起動することができます。まずはコマンドを入力して起動してみます。
では実際にやってみましょう。
Pythonのsubprocessを使うための準備
Pythonのsubprocessモジュールを使うためにはsubprocessモジュールをインポートする必要があります。
Pythonのコマンド待ち状態のときに下記のように入力すればsubprocessモジュールをインポートできます。ちなみにこれは使う前に1度だけ実行すればOKです。
1
|
>>> import subprocess
|
簡単な子プロセスの起動方法
Pythonのsubprocessモジュールのrun関数を使って子プロセスを起動します。
とりあえず「メモ帳」を起動してみることにします。下記のコマンドを実行してみてください。
1
|
>>> subprocess.run("notepad")
|
Windowsのシェルについて
Windows標準のシェルはコマンドプロンプト(cmd.exe)です。
コマンドプロンプトを開いてsetコマンドを実行し(「set」と入力してEnterキーを押せば表示されます)、環境変数ComSpecの設定を確認してみてください。デフォルト設定のシェルを確認できます。
シェルのdirコマンドを実行してみる
Windows標準のシェルであるコマンドプロンプト(cmd.exe)のdirコマンドを実行してみます。下記のコマンドを実行してdirコマンドが実行されるかを確認してください。
引数shellにTrueを指定していますが、これは「シェル(コマンドプロンプト)を起動して実行します」という意味です。dirコマンドは内部コマンドですのでコマンドプロンプトを起動しないと実行できません。ご注意ください。
また、subprocess.runでコマンドシェル用のコマンドを実行する場合には、内部コマンドか外部コマンドかに関わらず「shell=True」を指定しておいた方が不具合は出ません。
なお、subprocess.runには引数timeoutがあり、指定するとタイムアウトを設定できます。タイムアウトが発生するとTimeoutExpired例外が発生します。処理の仕方はPopen.communicateと同様ですので、次の項の「バックグラウンドによる実行」をご覧ください。
1
|
>>> subprocess.run("dir",shell=True)
|
バックグラウンドによる実行
Pythonのプログラム中から「子プロセスを起動したい」ときというのは大抵「バックグラウンドで実行したい」ときではないでしょうか。subprocessでバックグラウンド実行したいときはsubprocess.Popenを用います。
Popenを使って外部コマンドを起動するときはシェルを起動しない方が良いでしょう。理由はタイムアウトで子プロセスをkillしたいときなどのときに、シェルの子プロセス(つまり孫プロセス)になってしまうため、killメソッドではkillできなくなるからです。
Popenを使ったサンプルプログラム
下記のプログラムはWindowsのtimeoutコマンドを使った動作確認用プログラムです。
Popenに与えているコマンドはリスト型になっています。シェルを起動しないのでコマンドライン解析ができないためリスト型で与える必要があります。
Popenを使った行でバックグラウンド実行したいコマンドを実行しています。stdoutにsubprocess.PIPEを指定するとコマンド実行後に標準出力に出力された結果が得られます。「5」は5秒待つという意味のオプションです。適当に変えて実行してみてください。
communicateでtimeout指定すると指定時間経過しても子プロセスが終了しなかったときに例外のTimeoutExpiredが発生します。communicateを実行するとプロセスが終了するかタイムアウトするまで制御が戻ってこなくなりますので注意してください。
なお、print文で出力されるtimeoutコマンドの出力メッセージはエスケープシーケンスが使用されているため、メッセージの一部しか表示されません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# Python3.5以上で動作します。
# WindowsのPython3.9.1で動作確認しました。
import subprocess
import time
# 「5」はタイムアウト時間なので値を変更して動作させてみてください
proc = subprocess.Popen(["timeout","/t","5","/NOBREAK"],stdout=subprocess.PIPE)
# Pythonで実行したいプログラムの代わりのループ
for i in range(8): # 8の値を変えて実行時間を変えてみてください
time.sleep(1) # 1秒待つ
if proc.poll() is None: # Noneのときは子プロセス実行中のとき
fAlive = True
else: # None以外のとき、子プロセスは終了しています
fAlive = False
print(i+1,"秒, 子プロセス実行中 =",fAlive)
# pollで結果は得られていますが、communicateの使い方も示すためコードを含めておきます
try:
outs, errs = proc.communicate(timeout=1) # timeoutを1秒に設定
print("procは正常に終了しています。")
print(outs.decode("sjis"))
except subprocess.TimeoutExpired: # タイムアウトしたときここに来ます
print("procは実行中です。killします。")
proc.kill() # 子プロセスをkillします
outs, errs = proc.communicate()
print(outs.decode("sjis"))
|
こちらが実行結果になります。
1
2
3
4
5
6
7
8
9
|
1 秒, 子プロセス実行中 = True
2 秒, 子プロセス実行中 = True
3 秒, 子プロセス実行中 = True
4 秒, 子プロセス実行中 = True
5 秒, 子プロセス実行中 = False
6 秒, 子プロセス実行中 = False
7 秒, 子プロセス実行中 = False
8 秒, 子プロセス実行中 = False
procは正常に終了しています。
|
subprocessで起動した孫プロセスをkillする
普通にkillメソッドでkillすると子プロセスしかkillすることはできません。しかし、孫プロセスまでkillする方法があります。それはtaskkillという外部コマンドを使う方法です。
taskkillコマンドにPID(プロセスID)と適切なオプションスイッチを渡すことで孫プロセスまでkillすることができます。
下記のプログラムを実行すると15秒後に起動したシェルとtimeoutコマンドの2つのプロセスをkillします。なお、この方法はバッチファイルを起動したときなども有効です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# Python3.5以上で動作します。
# WindowsのPython3.9.1で動作確認しました。
import subprocess
import time
# 「30」はタイムアウト時間なので値を変更して動作させてみてください
proc = subprocess.Popen("timeout /t 30 /NOBREAK",shell=True,stdout=subprocess.PIPE)
print("少し待つ")
time.sleep(15) # 子プロセスが起動していることを確認するまでの時間を確保
if proc.poll() is None: # 子プロセスが起動しているとき
print("killします")
killcmd = "taskkill /F /PID {pid} /T".format(pid=proc.pid)
subprocess.run(killcmd,shell=True)
print("終了")
|
こちらが実行結果になります。
1
2
3
4
5
|
少し待つ
killします
成功: PID 10968 のプロセス (PID 3028 の子プロセス) を終了しました。
成功: PID 3028 のプロセス (PID 3012 の子プロセス) を終了しました。
終了
|
Pythonのsubprocessの使い方
subprocessのおすすめの使い方を紹介します。
まず子プロセスの実行完了待ちができる場合はsubprocess.runが手軽で良いと思います。バックグラウンドで実行したいときはsubprocess.Popenで起動して、適当なタイミングでPopen.pollメソッドで実行完了しているかを確認すると良いでしょう。
バックグラウンドで実行したとき、引数stdoutにsubprocess.PIPEを指定すれば実行完了時に標準出力に出力されたメッセージを取得することができます。
バックグラウンドで実行したとき、起動した子プロセスを強制終了するにはPopen.killメソッドを使用します。また、孫プロセスまで強制終了するにはtaskkillコマンドを使用します。
- システム
エンジニア - Pythonのsubprocessモジュールの使い方や子プロセスの起動方法、バックグラウンドでの実行方法がよく分かりました。
- プロジェクト
マネージャー - ご紹介した方法を参考にして、ご自身でソースコードを書いてみてください。
最後に
以上、subprocessの使い方について一通り解説しました。
子プロセスを制御できるようになれば、他の言語で作ったプログラム(.exeファイル)を起動して処理しながらPythonの方でも処理をすることができるようになりますので効率よく作業ができるようになるでしょう。
FEnet.NETナビ・.NETコラムは株式会社オープンアップシステムが運営しています。
株式会社オープンアップシステムはこんな会社です
秋葉原オフィスには株式会社オープンアップシステムをはじめグループのIT企業が集結!
数多くのエンジニアが集まります。
-
スマホアプリから業務系システムまで
スマホアプリから業務系システムまで開発案件多数。システムエンジニア・プログラマーとしての多彩なキャリアパスがあります。
-
充実した研修制度
毎年、IT技術のトレンドや社員の要望に合わせて、カリキュラムを刷新し展開しています。社内講師の丁寧なサポートを受けながら、自分のペースで学ぶことができます。
-
資格取得を応援
スキルアップしたい社員を応援するために資格取得一時金制度を設けています。受験料(実費)と合わせて資格レベルに合わせた最大10万円の一時金も支給しています。
-
東証プライム上場企業グループ
オープンアップシステムは東証プライム上場「株式会社オープンアップグループ」のグループ企業です。
安定した経営基盤とグループ間のスムーズな連携でコロナ禍でも安定した雇用を実現させています。
株式会社オープンアップシステムに興味を持った方へ
株式会社オープンアップシステムでは、開発系エンジニア・プログラマを募集しています。
年収をアップしたい!スキルアップしたい!大手の上流案件にチャレンジしたい!
まずは話だけでも聞いてみたい場合もOK。お気軽にご登録ください。
新着案件New Job
-
開発エンジニア/東京都品川区/【WEB面談可】/在宅ワーク
月給29万~30万円東京都品川区(大崎駅) -
遠隔テストサービス機能改修/JavaScript/東京都港区/【WEB面談可】/テレワーク
月給45万~60万円東京都港区(六本木駅) -
病院内システムの不具合対応、保守/東京都豊島区/【WEB面談可】/テレワーク
月給30万~30万円東京都豊島区(池袋駅) -
開発/JavaScript/東京都豊島区/【WEB面談可】/テレワーク
月給50万~50万円東京都豊島区(大塚駅) -
債権債務システム追加開発/東京都文京区/【WEB面談可】/在宅勤務
月給62万~67万円東京都文京区(後楽園駅) -
PMO/東京都豊島区/【WEB面談可】/在宅勤務
月給55万~55万円東京都豊島区(池袋駅)