Pythonをシェルスクリプトみたく使う
はじめに
Pythonって便利です.
っていう文言をいろんなところで書いていますが,Pythonの良いところは簡単なコードで色んなことができちゃうところです.
今回はその一つとして,「Pythonでシェルスクリプトみたいなことをやろう」です.
どんなときに/どんな人が使うのさ
例えば,
- C/C++やJavaなどで書いたプログラムの実行速度を計測してすぐにmatplotlibで図表を描画したいとき
- シェルスクリプト描いたことないけどPythonならすぐに書ける人
- Pythonが大好きすぎる人
です.
いや,多分ほかにも正しい使い方はあるとは思いますが...
subprocess
Pythonのsubprocessというモジュールは,実際にシェルコマンドを打鍵して結果を得るように,コマンドを実行してその出力やエラーなどを取得できたりする.
今回紹介するコードはPython3.5以降で動作するのでご了承いただきたい.
単純な例
まずは簡単なコードから.
基本は以下の記法でシェルコマンドを実行可能である.
1 2 3 4 5 6 7 8 9 | import subprocess if __name__ == '__main__': # コマンドを実行し,その結果を保持するプロセスを取得 res = subprocess.run('ls', stdout=subprocess.PIPE) # そのうち出力情報をUTF-8にデコードして出力 print(res.stdout.decode('utf8')) |
subprocess.run で返ってくるものは,出力結果そのものというわけではなく, CompletedProcess クラスという出力情報やエラー情報,投げたコマンドの情報を持ったクラスが返る.
その際,欲しいものは引数で subprocess.PIPE を渡す必要がある.
ちなみに, res.args で投げたコマンドを参照可能.
オプション指定
例えば, ls -a を実行したい時には,以下のようにリストで渡す必要がある.
1 | res = subprocess.run(['ls', '-a'], stdout=subprocess.PIPE) |
'ls -a' はダメ.
なお,以下の様に複数コマンドを同時に実行などはできない.
1 | res = subprocess.run(['ls', 'pwd'], stdout=subprocess.PIPE) # これはダメ |
もし,複数コマンドを実行したければ,その都度 run() を呼ぶ.
実行ディレクトリを指定
どこで呼び出しても,所定のディレクトリでそのシェルコマンドを実行したいときは以下のようにする.
1 | res = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, cwd='/Users/araki') |
ちなみに,僕の環境では ~ は使えなかった.
もし,どうしても動的なホームディレクトリからパスを実行したいのなら,以下のような書き方があるようだ.
1 2 | import os res = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, cwd=os.path.expanduser('~')) |
1 2 | import pathlib res = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, cwd=pathlib.Path.home()) |
ちなみに, str(pathlib.Path.home()) のほうが正しい.
実験用ファイル例
ここからは実用例.
想定としては,とある実行ファイルの実行時間を調査したい時に書くコード.
実験背景の詳細は以下.
- 実行ファイルは「example」
- 引数を一つ取り,その引数によって実行時間に変化がある
- 実行結果は特に必要ない
- 10試行分とり,平均および誤差を調査したい
そうすると,こんなコードでしょうか.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import numpy as np import matplotlib.pyplot as plt from time import time import subprocess if __name__ == '__main__': trial = 10 # 試行回数 results = [] # 結果を格納するリスト expt_range = np.arange(10, 100, 10) # 実行ファイルの引数 for _ in range(10): result = [] for i in expt_range: args = ['./example', f'{i}'] # 実行コマンド start = time() # 計測スタート subprocess.run(args) # 実行! result.append(time() - start) # 計測時間を格納 results.append(result) results = np.array(results) # 結果をプロット _mean = np.mean(results, 0) # 全試行分の平均値 _std = np.std(results, 0) # 全試行分の標準偏差 plt.plot(expt_range, _mean, c='tab:blue', marker='.') plt.errorbar(expt_range, _mean, _std, c='tab:blue') plt.show() |
このように,実行からグラフ描画までPythonファイル一つで済むのは良いところです.
ただし,かなり精密に実行時間を計測するのであれば, run() 内でのオーバーヘッドがありますので,書いといて言うのもあれですがお勧めできません.
もし,より正確な実行時間が欲しければ,普通にシェルスクリプト書くか,C++のchronoを使った方が良いだろう.
あとがき
今回は,Pythonでシェルスクリプトを書いてみる,という記事でした.
実際,使う機会はあまりないかもしれませんが,CPU温度やGPUメモリ使用率などを監視するときとかには使えるかもしれません.
なんかもっとうまい使い方ないかな.
- タグ:
- Python