pythonライブラリにはド定番のライブラリがいくつもあるが、それらの仕様は結構忘れがち。
たとえばcsvのDictWriterクラス。引数がファイルオブジェクトなのかそれともパスを文字列で与えるのか、刹那の思考をはさんだ後にwith open()などと書き始める。
csv.DictWriterクラスならまだ良いが、やり方が複数通り存在する場合になるともうカオスだ。urllib.requestとrequestsのどっち使うんだとか、はたまたurllib3はなんなんだとか。。そんな「結局何が正解なん」案件でsubprocessの使い方についてちょっと思うところがあった。
色々やりたいときはPopenクラスを使う、簡単なユーズケースならrunメソッドを使う、という認識でいたので、基本的に何も考えずPopenクラスを毎回使っていた。
こんな感じ。
from subprocess import Popen
ps = Popen(['ls', '-lah'])
ps.wait()Popen.wait()するタイミングをずらすことで非同期処理的にマルチプロセス化することもあるため、個人的にはシェルスクリプトとしてpythonを使う際には欠かせないやつ🤔
標準出力を取得しようとするとsubprocess.PIPEが必要になるのでちょいめんどくさい。バイト文字列で選ってくるところがなおさら😅
from subprocess import Popen, PIPE
ps = Popen(['echo', 'hello'], stdout=PIPE)
s_out = ps.stdout.readlines()
print([s.decode("utf-8") for s in s_out])['hello\n']こちらのQiita記事を拝読すると、「suborocess.runを使うのが良いとされている」とか「capture_outputを使ってもできる」というのを聞いて👀から🐟!
import subprocess
ps = subprocess.run(['echo', 'hello'], capture_output=True, text=True)
s_out = ps.stdout
print(s_out)hello—はぁ・・・勉強不足でした。。すみません。。と思いつつdocumentを見ると確かに以下のようにrunメソッドが推奨されていた。1
サブプロセスを起動するために推奨される方法は、すべての用法を扱える run() 関数を使用することです。より高度な用法では下層の Popen インターフェースを直接使用することもできます。
また、capture_outputオプションはpython3.7で実装されたらしい。なるほど比較的新しいけど、少なくともセキュリティサポートされているバージョンをきちんと追ってる人なら間違えてはいけないポイントだろう。
結論、Qiita記事はちょっと危険だなと思った。自分も似たようなものだが・・・
shell=Trueを使えば簡単にパイプが使えるので、便利なことは便利。しかし、任意のコードを実行させられる可能性があるので、ウェブフレームワークなどで使うのは御法度というのが有名。
これを説明せずに「shell=Trueの方が初心者にはオススメ」などと平然と書かれているケースがあった。
先に言っておくが、個人ブログは高確率でこれに言及している。最初の投稿時に言及が無くとも、ほぼ全員が追記していた。すごい😲きっと多分自分のコンテンツに責任を持っているのだろう。自分もそうならねば。
Popen.PIPEの方法はQiita記事だけをみていたらゴールドスタンダードに見えるだろう。現に自分がそうだった。
正直自分はpythonドキュメントきちんと読んでこなかった。だってフォントとかレイアウトとかのせいか、読みにくいんだもん。
しかし、細かい推奨レベルや後で追加されたオプションなど、知らないと恥かく内容も多いと感じた。
コツコツpythonドキュメントをちゃんと読み直してみるのもいい学習方法かもしれない。