Pythonからコマンドを実行する方法(subprocessモジュール)

Pythonで自前で処理を書くより、OSのコマンドを実行したほうが簡単な場合がよくあります。そのようなとき、Pythonからコマンドを実行するためにはsubprocessモジュールを使います。

subprocessモジュールを使うには、次のようにをモジュールをインポートします。

import subprocess

subprocessモジュールはコマンドの実行に限られません。任意の実行可能プログラムを走らせることができます。そのためこの記事でコマンドと記載している箇所は、プログラム(プロセス)と読み替えてもらっても構いません。

最初にコマンドを実行するための基本的なことを一通り説明し、その後subprocessモジュールの他の機能について説明します。

コマンド(プログラム)実行の基本

コマンドを実行する

Python 3.5からコマンドの実行は、run()関数を使うことが推奨されています。コマンドを実行するための最も単純な書式は次の通りです。

run(args)

run()関数はargsで指定されたコマンドを実行し、コマンドの完了を待ってCompletedProcessオブジェクトを返します。argsは1つの文字列か1つのリストです。

1つの文字列を指定する場合、コマンドの名前(あるいはコマンドのパス)である必要があります(オプションは指定できません)。

subprocess.run("ls")

コマンドのオプションも指定する場合は、リストで指定します。リストの先頭がコマンド名と解釈されます。

subprocess.run(["ls", "-l"])

コマンドをシェルで実行する

run()関数のデフォルトでは、Pythonが直接コマンドを実行しますが、コマンドをシェルで実行することもできます。それにはrun()関数に「shell=True」を指定します。

シェルで実行する場合は、1つの文字列で「シェルに打ち込む通り」にコマンドとオプションを渡す必要があります。

subprocess.run("ls -l", shell=True)

シェルで実行すればシェルの機能が使えます。つまりシェルのワイルドカード文字、リダイレクト、パイプなどです。

subprocess.run("ls -l su*", shell=True)
subprocess.run("ls -l > foo.txt", shell=True)
subprocess.run("ls -l | wc", shell=True)

なおshell=Trueの場合に実行されるシェルのデフォルトは、/bin/sh(Bourne Shell)です。

コマンドの終了ステータス(リターンコード)を確認する

コマンドは、正常に終了したかどうかを示す終了ステータスを呼び出し側に返します(通常0は正常終了を意味する)。この終了ステータスは、run()関数が返すCompletedProcessオブジェクトのreturncode属性に保存されます。

したがって、コマンドが正常終了したかどうかを確認するには、このreturncode属性を参照します。

>>> cp = subprocess.run(["ls", "-l"])
...
>>> print("returncode:", cp.returncode)
returncode: 0

コマンド実行結果の出力先は?

コマンドの標準出力と標準エラー出力の出力先は、run()関数のstdoutとstderr引数で変更できます。デフォルト値はどちらもNoneです。

Noneの場合は、親プロセス(つまりPythonプログラム)から引き継がれます。つまりPythonプログラムをシェルで実行すれば、コマンドの標準出力と標準エラー出力の結果はシェルの画面に出力されます。

この後、出力先を変更する例をいくつか紹介します。

コマンドの実行結果をPythonで取得する

コマンドの結果を取得する

Python側でコマンドの出力結果を取得するには、run()関数に「capture_output=True」を指定します。

「capture_output=True」を指定すると、内部のPopenオブジェクトが自動的にstdout=subprocess.PIPEとstderr=subprocess.PIPEを設定します。そのためコマンドの標準出力と標準エラー出力のデータはPIPEへ送られます。

PIPEへ送られたデータは、CompletedProcessオブジェクトのstdout、stderr属性で参照できます。

>>> cp = subprocess.run("ls", capture_output=True)
>>> print("stdout:", cp.stdout)
stdout: b'foo.txt\nsubp.py\n'
>>> print("stderr:", cp.stderr)
stderr: b''

コマンドから標準エラー出力へは出力が無いため、stderrは空文字列です。この例から分かる通り、デフォルトで出力はバイト文字列になります。テキストで取得する方法は、この後に説明します。

capture_output引数はバージョン3.7で追加されました。それ以前のバージョンの場合は、run()関数のstdout、stderr引数にsubprocess.PIPEを手動すれば良いでしょう。

subprocess.run("ls", stdout=subprocess.PIPE, stderr=subprocess.PIPE)

結果をテキストで取得する

stdoutとstderrに指定されるファイルオブジェクトは、デフォルトでバイナリモードで開かれるため、出力結果もバイナリ文字列(bytes型)になります。

テキスト文字列で取得するには、run()関数に「text=True」も指定します。

>>> cp = subprocess.run("ls", capture_output=True, text=True)
>>> print("stdout:", cp.stdout)
stdout: foo.txt
subp.py
      

text引数はバージョン3.7で追加されたので、バージョン3.6ではencoding引数を使います。

cp = subprocess.run("ls", capture_output=True, encoding="utf-8")

encoding引数はバージョン3.6で追加されたようですので、それ以前は。。。、各自で確認をお願いいたします。

環境変数

コマンドを実行するときの環境変数は、run()関数のenv引数で指定できます。envのデフォルト値はNoneです。Noneの場合は親プロセス(つまりPythonプログラム)の環境変数を引き継ぎます。したがって、通常は次のようにコマンド名だけでコマンドが実行できます。

subprocess.run("ls")

env引数については、これ以上は触れません。

その他の使い方

標準エラー出力を標準出力へ向ける

標準エラー出力を標準出力を同じところへ出力させるには次のようにします。

cp = subprocess.run("ls", stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

「stderr=subprocess.STDOUT」を指定すると、コマンドの標準エラー出力は標準出力と同じところへ出力されます。この例では、「stdout=subprocess.PIPE」も指定されていますので、標準出力と標準エラー出力が混ざった出力が、CompletedProcessオブジェクトのstdout属性で参照できるようになります。

コマンドの出力結果を捨てる

コマンドの出力結果が必要ない場合は、stdoutやstderrにsubprocess.DEVNULLを指定します。

subprocess.run("ls", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

この例では標準出力と標準エラー出力の両方ともDEVNULLに送られます。DEVNULLはnullデバイス(Linuxなら/dev/null)を意味するので、出力は黙って捨てられます。

コマンドの標準入力にデータを渡す

コマンドの標準入力にデータを渡したい場合は、input引数にデータを指定します。このとき、内部Popenオブジェクトは自動的にstdin=PIPEを作成します。

input引数へのデータは、デフォルトではバイト文字列である必要があります。

>>> cp = subprocess.run("cat", input=b"foo\nbar\n")
foo
bar

テキスト文字列を渡したい場合は「text=True」も追加します。

subprocess.run("cat", input="foo\nbar\n", text=True)

CompletedProcessオブジェクトの完全な属性の一覧

run()関数でコマンドの実行が完了するとCompletedProcessオブジェクトが返されます。このオブジェクトの属性にはコマンド実行に関するすべての情報が格納されています。

args以外は既に説明しましたが、重要な情報なので改めてすべての属性を確認してみましょう。

属性 説明
args コマンド起動のに使用された引数
retruncode コマンドの終了ステータス。0は一般的に正常終了したことを表す
stdout stdout=subprocess.PIPEが指定された場合、コマンドの標準出力のデータを格納される。PIPEが指定されなければNoneになる
stderr stderr=subprocess.PIPEが指定された場合、コマンドの標準エラー出力のデータを格納される。PIPEが指定されなければNoneになる

次の例はCompletedProcessオブジェクトのすべての属性を出力しています。

>>> cp = subprocess.run(["ls", "-l"])
...
>>> print("args:", cp.args)
print("args:", cp.args)
>>> print("returncode:", cp.returncode)
returncode: 0
>>> print("stdout:", cp.stdout)
stdout: None
>>> print("stderr:", cp.stderr)
stderr: None

コマンドがエラー終了した場合にCalledProcessError例外を送出する

run()関数に「check=True」を指定すると、コマンドが0以外の終了ステータスで終了した場合、CalledProcessError例外を送出します。

CalledProcessErrorには属性returncode、cmd、output、stdout、stderrがあります。属性の意味はCompleteProcessオブジェクト同じです。補足するとcmdはCompleteProcess.argsと同じ、stdout属性はoutput属性の別名です。

次のコードは、CalledProcessErrorのすべての属性を出力します。

import subprocess

try:
    cp = subprocess.run(["ls", "hoge.txt"], check=True)
except subprocess.CalledProcessError as err:
    print("returncode:", err.returncode)
    print("cmd:", err.cmd)
    print("output:", err.output)
    print("stdout:", err.stdout)
    print("stderr:", err.stderr)

subp.pyに記載して実行すると次のように出力されます。

# python3 subp.py
ls: 'hoge.txt' にアクセスできません: No such file or directory
returncode: 2
cmd: ['ls', 'hoge.txt']
output: None
stdout: None
stderr: None

おまけ

コマンドをシェルで実行したときの詳細を確認してみた

コマンドをシェルで実行される様子を実際に確認してみました。

import subprocess

subprocess.run("ls; sleep 60", shell=True)
print("Command end")

上記のコードをsubp.pyに記載して、バックグラウンドで実行します。

# python3 subp.py &
...

そして60秒以内にpstreeでプロセスの親子関係を確認します。

# pstree
systemd─┬─ModemManager───2*[{ModemManager}]
        ...
        ├─sshd───sshd───sshd───bash─┬─pstree
        │                           └─python3───sh───sleep
        ...

pstreeの出力からpython3の子として/bin/shが実行され、その子としてsleepコマンドが実行されているのがわかります。

ただし、最初は次のコードで実行しましたが、/bin/shの実行は確認できませんでした。

subprocess.run("sleep 60", shell=True)

pstreeの出力は次の通りです。

    # python3 subp.py &
    ...
    # pstree
    systemd─┬─ModemManager───2*[{ModemManager}]
    ...
    ├─sshd───sshd───sshd───bash─┬─pstree
    │                           └─python3───sleep
    ...

シェル(/bin/sh)で実行する必要がないとPythonが判断して、Pythonが直接sleepコマンドを実行したのでしょうか?これは謎です。

投稿日:

Copyright© アナグマのモノローグ , 2024 All Rights Reserved Powered by STINGER.