subprocessを使って外部プログラムを実行する

https://docs.python.jp/3/library/subprocess.html

subprocessには色々な関数が定義されているのでとりあえずいくつか使いそうなものを試してみます。

例としてPythonから別のPythonのプログラムを実行する処理を実装してみます。

subprocess.check_callによる簡易的な外部プログラムの実行

まずtest.pyを用意します。

# -- test.py --
from time import sleep

print('child start!')
sleep(1)
print('child end!')

これをmain.pyから実行してみます。

# -- main.py --
import subprocess

print('parent start!')
subprocess.check_call(['python','test.py'])
print('parent end!')
$ py main.py
parent start!
child start!
child end!
parent end!

check_allは子プロセス(test.py)の終了をまってから次の処理へ進みます。

また外部プログラム側で何かしら例外で終了した場合はcheck_callでも例外を投げます。

# -- error.py --
raise Exception('error test!')
# -- main.py --
import subprocess

print('parent start!')
subprocess.check_call(['python','error.py'])
print('parent end!')
$ py main.py
parent start!
Traceback (most recent call last):
  File "error.py", line 1, in <module>
    raise Exception('error test!')
Exception: error test!
Traceback (most recent call last):
  File "main.py", line 8, in <module>
    ps = subprocess.check_call(['python','error.py'])
  File "F:\Python36\lib\subprocess.py", line 291, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['python', 'error.py']' returned non-zero exit status 1.

subprocess.check_outputで実行されたプログラムの標準出力を得る

check_callと違い、こちらは外部プログラムで実行された標準出力を実行側で文字列で取得することができます。

# -- test.py --
from time import sleep

print('child start!')
sleep(1)
print('child end!')
# -- main.py --
import subprocess

output = subprocess.check_output(['python','test.py'])

print('---')
print(output.decode('sjis'))
$ py main.py
---
child start!
child end!

ちなみにcheck_outputの戻り値はバイナリ文字列になってるのでdecodeで文字列化してから出力してます。

subprocess.runによる外部プログラムの実行

Python3.5以上からはcheck_callやcheck_outputよりも柔軟なrunを使うこともできます。

check_callをrunで置き換えると以下のようになります。

import subprocess

print('parent start!')
subprocess.run(['python','test.py'], check=True)
print('parent end!')


check_outputをrunで置き換えると以下のようになります。

import subprocess

output = subprocess.run(['python','test.py'], check=True, stdout=subprocess.PIPE).stdout
print(output.decode('sjis'))

他にも色々できるようですが、ここでは割愛。

目的がハッキリしている場合はcheck_callやcheck_outputでも良さそうですね。

subprocess.Popenによる詳細な外部プログラムの実行

通常ではcheck_allやcheck_output(およびrun)などを使えばそれで基本的には問題がでることはありませんが、例えば子プロセスの終了をまたずに何か処理をしたいとかそういうことをしたい場合にはPopenを使います。

# -- test.py --
from time import sleep

print('child start!')
sleep(1)
print('child end!')
# -- main.py --
import subprocess

print('parent start!')
ps = subprocess.Popen(['python','test.py'])
print('parent end!')
$ py main.py
parent start!
parent end!
child start!
child end!

このように子プロセスの終了をまたずに「parent end!」が出力されていますね。