ラズパイ:Pythonからシェルの実行結果を文字列で取得したい

スポンサーリンク
ラズベリーパイのロゴ Raspberry Pi

Raspberry PiでPythonからシェルの実行結果を文字列として取得するには、subprocessクラスを使います。実際の例を以下に示します。

subprocess --- サブプロセス管理 — Python 3.9.4 ドキュメント

スクリプトの実際例

Python3.5以上で利用可能。もしPython3.5以下の環境であれば《Python3.5以前で実行する場合のスクリプト》の項をご覧ください。

subprocess_test.py

#!/usr/bin python3
# -*- coding: utf-8 -*-

"""
	ラズパイ(Raspberry Pi)でPythonからシェルの実行結果を文字列で取得したい
		2021-08-27
		
		python 3.5.3
		OS: Raspbian 9.13 stretch(Raspberry Pi2)にて確認
"""
import subprocess

# 実行したいシェルコマンド
cmd = "df -h"

# シェルをPythonから実行
proc = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)

# シェルの実行結果を取得
text_origin = proc.stdout.read()

# バイトリテラル(bytes)として実行結果が返されるため文字列(string)にデコード
text = text_origin.decode("utf-8")

# 実行結果を出力
print(text)
print(proc.pid)

実行イメージ(ディスクの空き容量が表示される)
※お使いの環境により表示は変わります

pi@raspberrypi ~ $ python3 subprocess_test.py
ファイルシス   サイズ  使用  残り 使用% マウント位置
rootfs           6.3G  5.4G  594M   91% /
/dev/root        6.3G  5.4G  594M   91% /
devtmpfs         428M     0  428M    0% /dev
tmpfs             87M  752K   86M    1% /run
tmpfs            5.0M     0  5.0M    0% /run/lock
tmpfs            173M     0  173M    0% /run/shm
/dev/mmcblk0p5    60M   19M   42M   32% /boot

解説

シェルをPythonから実行するためにsubprocessクラスをインポートします。

import subprocess

実行したいシェルコマンドを変数cmdに文字列として代入しています。

cmd = "df -h"

例えば、ls -l を実行したいのであれば、 cmd = “ls -l” とします。

シェルコマンドをsubprocess.Popenコマンドに渡します。

proc = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)

subprocess.Popenの第1引数はシェルコマンドを空白で区切って実際は cmd = [“df”, “-h”] のようにリストで渡す必要があるのですが、シェルコマンドが分かりにくくなるので、cmd.split() として元の文字列をリストに変換して渡しています。

第2引数に設定した stdout=subprocess.PIPE を省略してしまうとPython実行中にシェル実行結果が表示されてしまい文字列として取得できなくなるため必要です。

戻り値procのstdout.read()でシェルの出力結果を取得できます。
ただしこの戻り値はバイトリテラル(bytes)のため、そのまま利用はしません。

text_origin = proc.stdout.read()

ちなみに変数text_originをそのまま出力すると日本語などの文字は全て \xe3 などと変換されてしまい非常に見にくいし使いにくいです。

b'\xe3\x83\x95\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab\xe3\x82\xb7\xe3\x82\xb9   \xe3\x82\xb5\xe3\x82\xa4\xe3\x82\xba  \xe4\xbd\xbf\xe7\x94\xa8  \xe6\xae\x8b\xe3\x82\x8a \xe4\xbd\xbf\xe7\x94\xa8% \xe3\x83\x9e\xe3\x82\xa6\xe3\x83\xb3\xe3\x83\x88\xe4\xbd\x8d\xe7\xbd\xae\nrootfs           6.3G  5.4G  594M   91% /\n/dev/root        6.3G  5.4G  594M   91% /\ndevtmpfs         428M     0  428M    0% /dev\ntmpfs             87M  752K   86M    1% /run\ntmpfs            5.0M     0  5.0M    0% /run/lock\ntmpfs            173M     0  173M    0% /run/shm\n/dev/mmcblk0p5    60M   19M   42M   32% /boot\n'

そのため次の処理としてデコードが必要になります。

バイトリテラルを文字列にデコードしてシェルの実行結果の文字列取得が完了となります。

text = text_origin.decode("utf-8")

もし、Python3.5以前の環境をお使いなら次のスクリプトが役に立つかもしれません。

Python3.5以前で実行する場合のスクリプト

subprocess.check_outputを使うと戻り値がそのままシェルの実行結果となります。
ただし、subprocess.Popen同様にバイトリテラルのためデコードは必要です。

このスクリプトは、Python3.5以上でも実行できます。ただし、利用している subprocess.check_output コマンドはPython公式マニュアルでは古い高水準APIこちら)として紹介されているため、今後無くなる可能性があります。

subprocess_test_old.py

#!/usr/bin python3
# -*- coding: utf-8 -*-

"""
	ラズパイ(Raspberry Pi)でPythonからシェルの実行結果を文字列で取得したい
		2021-08-27
	
		Python3.5以前の記述

"""
import subprocess

# 実行したいシェルコマンド
cmd = "df -h"

# シェルをPythonから実行
text_origin = subprocess.check_output(cmd.split())

# バイトリテラル(bytes)として実行結果が返されるため文字列(string)にデコード
text = text_origin.decode("utf-8")

# 実行結果を出力
print(text)

その他補足

subprocess以前では、osモジュールをインポートして、次のようにシェルの実行結果を取得できました。(今も出来ますが)

import os

cmd = "ls /"
result = os.popen(cmd).read()
print(result)

実行イメージ

bin
boot
dev
etc
home
lib
lost+found
media
mnt
opt
proc
radio
root
run
sbin
srv
sys
tmp
usr
var

もちろんこれで十分ですが、os.popenなどは今後なくなる命令のため今回は紹介しませんでした。

また、subprocessを使うメリットはシェルのプロセスIDが取得できる点です。
プロセスIDが取得できれば、Pythonスクリプトから呼び出したシェルを終了したりなどと操作できます。これが一番大きなメリットだと個人的には思います。

コメント

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