Python3 の他プロセス呼び出しライブラリ subprocess の典型的使用法のメモ.詳細は公式ドキュメント参照.
単にプロセスを起動
run メソッドに,起動するコマンド (と引数からなるリスト) を渡す.ステータスは戻り値の returncode 属性で参照できる.
たとえば,ls コマンドを起動して,出力は画面に出せば良いという場合:
import subprocess, sys
cp = subprocess.run(['ls', '-a'])
if cp.returncode != 0:
print('ls failed.', file=sys.stderr)
sys.exit(1)
出力を取得
値として出力を取得
コマンドの標準出力を取得したい場合には,runメソッドの stdout 引数に subprocess.PIPE を渡す.復帰値のstdout属性で取得できる.
cp = subprocess.run(['ls', '-1'], encoding='utf-8', stdout=subprocess.PIPE)
print(f'*** file names: \n{cp.stdout}'
f'*** total {len(cp.stdout.splitlines())} files')
上のように encoding='utf-8'
を指定すると,文字列として取得できる.他にも文字列として取得するオプションがある.(このへんは,Python 3.5と3.6では,けっこう仕様が変わっている.) そういうものを指定しないと,バイト列として受け取ることになる.
標準エラー出力を取得したい場合には,当然,stderr=subprocess.PIPE
を指定して,復帰値のstderr属性を参照する.標準エラー出力と標準出力を混ぜて取りたい場合 (2>&1
相当) には,stdout=subprocess.PIPE stderr=subprocess.STDOUT
と指定して,復帰値のstdout
属性を参照すれば良い.
出力をファイルに書き出す
file object を与えればよい:
with open('out.txt', 'w') as fp:
cp = subprocess.run(['ls', '-1'], stdout=fp)
入力を与える
文字列 (や,バイト列) で与えるときには,runメソッドの input 引数を用いる.
cp = subprocess.run(['wc'], input='abc def ghijkl', encoding='UTF-8')
ファイルから読ませるときには,引数stdin に,file object を与える.
with open('q.txt', encoding='UTF-8') as fp:
cp = subprocess.run(['wc'], stdin=fp)
継続的に実行
runメソッドは,ブロックする.すなわち,指定したコマンドが終了してはじめて制御がPythonスクリプトに戻ってくる.コマンドを動かしながらスクリプト側でも処理をしたい場合には,Popenオブジェクトを使う.作成するときに stdin, stdout, stderr に subprocess.PIPE を指定しておけば,Popenオブジェクトの stdin, stdout, stderr 属性がファイルハンドルとして使用できる.
このとき,with 構文も使える.withブロックを抜ける時に,Popenオブジェクトは,コマンドの標準入出力を閉じて,コマンドのプロセスが終了するのを待つ.
lst = [(4, 3), (2, 1), (3, 9)]
with subprocess.Popen(['bc', '-i'], encoding='UTF-8',
stdin=subprocess.PIPE, stdout=subprocess.PIPE) as proc:
while True:
msg = proc.stdout.readline()
if msg[:11] == 'For details':
break
for (x,y) in lst:
print(f'{x} + {y}', file=proc.stdin, flush=True)
proc.stdout.readline()
z = proc.stdout.readline().strip()
print(f'{x} + {y} = {z}', file=sys.stderr)
したがって,起動しているコマンドが,(標準入出力を閉じられたとしても気にせず) 終了しないとすると,with のブロックから抜けてこないことになる.ここでデッドロックになってしまわないよう注意を払う必要がある.