Raspberry PiでPythonからシェルの実行結果を文字列として取得するには、subprocessクラスを使います。実際の例を以下に示します。
スクリプトの実際例
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スクリプトから呼び出したシェルを終了したりなどと操作できます。これが一番大きなメリットだと個人的には思います。
コメント