JP / EN

広告
2024/11/28

Pythonからシェルコマンドをsubprocessとして呼びだす

タグ:python linux

Pythonでプログラムを作成していて、途中で外部のプログラムを呼び出すなどしたいことがままある。 Python用のAPI等を用意してくれていればよいのだが、そうとも限らないのでシェルのコマンドとして 外部プログラムを呼び出してしまい、結果だけもらうのが早いこともある。
このときはsubprocessパッケージを使えば、Pythonスクリプトとしてコマンド実行を含むような一連の手続きを 完結させることができる。

    In [1]: import subprocess
    In [2]:  subprocess.run(["echo", "hey"])
    hey
    Out[2]: CompletedProcess(args=['echo', 'hey'], returncode=0)
  
返り値は実行されたプロセスを表すオブジェクトとなる。 リターンコード(0なら成功、1なら失敗)は含まれるものの、 デフォルトでは プロセスがプリントした標準出力などは見ることはできない。 わざわざ外部コマンドを実行するからには、これが欲しいこともあるだろう。 そのようなときは capture_output=Trueを渡すと標準出力と 標準エラーをプロセスオブジェクトが保持してくれる。
    In [3]:  subprocess.run(["echo", "hey"], capture_output=True)
    Out[3]: CompletedProcess(args=['echo', 'hey'], returncode=0, stdout=b'hey\n', stderr=b'')
  
のように出力オブジェクトにstdout/errが増えているのでこれを後で参照できるようになる。

またコマンドが単一のスペース区切り文字列ではなく、単語のリストでないといけないのが気になる人もいるかもしれない。 これはどこかから取得された適当なテキストが放り込まれたときに変数展開やクオーテーション・ダブルクオーテーション周りの扱いでミスが起きる、 また特に外部から受けとったコマンドを使う際にこのせいでコマンドインジェクション攻撃を受けることを予防するためのものである。 Dockerfileでもこのようなコマンドの書き方は使われており、exec形式と呼ばれている。
これに対して"hey hoge"文字列でコマンドを指定するのはshell形式と呼ばれる。 subprocessにshell形式でコマンドを渡したいときは引数にshell=Trueを渡してやれば良い。
    In [4]:  subprocess.run("echo hey", capture_output=True, shell=True)
    Out[4]: CompletedProcess(args=['echo', 'hey'], returncode=0, stdout=b'hey\n', stderr=b'')
  

なおshell形式でもシェルで許される構文のすべてがsubprocessで使えるわけではない。 例えばfor文や、evalとは``によるサブコマンドの評価などは使えないようである。 このような込み入った操作はPython側でコマンドを動的にビルドするなどして対応するのが良いだろう。

おすすめ記事

h5ファイルをPythonで開いて中身を確認する

FastAPI入門:最小構成でとりあえずサーバを立ててみる

型アノテーション:Python3.8以下でTypeError: 'type' object is not subscriptableがでる



このエントリーをはてなブックマークに追加

https://wonderhorn.net/