PythonでOSコマンドを実行する(subprocess.run利用)

Python

PyhonでOSコマンドを実行する方法はバージョンによって色々あるのですが、今回はsubprocess.runの利用方法をまとめてみます。Pythonは3.8を前提としています。

基本

subprocess.runは引数のコマンドを同期処理で実行します。
コマンドをそのまま文字列として実行したい場合は、「shell=True」を指定します。
可読性は高くなりますが脆弱性にもつながるので利用には要注意です。

import subprocess

subprocess.run(['ls', '-al'])

command = 'ls -al'
ret = subprocess.run(command, shell=True)
print(ret)

上記の戻り値の出力は以下のような簡単なものになります。

CompletedProcess(args='ls -al', returncode=0)

戻り値の確認と標準出力、エラー出力のキャプチャ

「capture_output=True」のオプションを利用すると標準出力、エラー出力を
キャプチャすることが出来ます。(Python3.7以降)

戻り値の「ret.stdout」「ret.stderr」で参照に可能になりますが
この戻り値はバイトデータなのでdecodeが必要です。
「text=True」のオプションをつけるとdocodeした結果が格納されます。

戻り値は「ret.returncode」で参照が可能です。

command = 'ls -al'
ret = subprocess.run(command, shell=True, capture_output=True, text=True)
print(ret.returncode)
print('stdout:' + ret.stdout)
print('stderr:' + ret.stderr)

例外処理

コマンドの実行結果を1つ1つチェックする代わりに例外処理を利用することも出来ます。
「check=True」のオプションを指定すると実行結果が0以外の場合に
「subprocess.CalledProcessError」例外がスローされるようになります。

例外オブジェクトの「stderr」でエラー出力の参照、「cmd」で実行コマンドを参照することが可能です。

import subprocess

try:
    command = 'mkdir @/@'
    ret = subprocess.run(command, shell=True, capture_output=True, text=True, check=True)
    print(ret.returncode)
    print('stdout:' + ret.stdout)
except subprocess.CalledProcessError as cpe:
    print('returncode:' + str(cpe.returncode))
    print('stderr:'     + cpe.stderr)
    print('cmd:'        + cpe.cmd)
    raise cpe

まとめ

今までの内容を本番実装に近い形でまとめてみます。
subprocess.runは関数化して実行コマンドをログで残すようにしました。

import subprocess
import traceback
import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

def process_run(_command):
    logger.info('subprocess.run: ' + _command)
    ret = subprocess.run(_command, shell=True, 
        capture_output=True, text=True, check=True)
    return ret

def main_func():
    try:
        process_run('ls -al')
    except subprocess.CalledProcessError as cpe:
        logger.error('subprocess err. ' + cpe.stderr + '\n' + 
            'returncode: ' + str(cpe.returncode) + '\n' +
            'cmd: ' + cpe.cmd)
        raise cpe
    except Exception as e:
        traceback.format_exc()
        raise e

if __name__ == '__main__':
    main_func()

以上

タイトルとURLをコピーしました