それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

pythonでコマンド実行するには

シェルスクリプトの代わりにPythonで書いてみる

コマンド実行をする

コレを使います。

import subprocess
import shlex
ret = subprocess.check_output(shlex.split("date -I")).decode("UTF-8").strip()
print(ret)

詳しく書いていきます。

subprocess モジュールを使う

subprocess はコマンド実行がちょっと面倒臭い。

import subprocess

input_f   = "player.swf"
output_f = "radiko.png"

cmd = "/usr/local/bin/swfextract -b 14 %s -o %s" % (input_f, output_f )
subprocess.call( cmd.strip().split(" ")  ) 

subpcorcess.call は配列でコマンドとコマンドの引数を与える。 面倒なので、コマンド文字列を作ってから、split(" ") で文字列を分割して配列にしたら楽かも。

シェル経由で実行する

シェル経由で実行すると楽ちんですよね。

bash の場合

バッククォート演算子を使って文字列を囲うだけで出来る。

`wget -O index.html http://www.yahoo.co.jp `

python をシェル経由で実行するには

call に shell=True のキーワード引数をつけるだけ。

import subprocess
cmd = "ls -lt | head -n 5"
subprocess.call( cmd, shell=True  ) 

これで、シェル経由実行される

subprocess.call([cmd,arg1,arg2..], shell=True)は 旧来のos.system の実行と同じ

import os
cmd = "ls -lt "
os.system( cmd )

os.system はすでにサポートされなくなりつつあるので、subprocessを使う方がいいようです。

シェル経由とは

シェル経由で実行するとは、こういうこと。

sh -c "コマンド"

のように、sh -c をつけて実行していることに相当する。

コマンドの文字列をそのまま渡せるので、Shell=Trueを使う人が多いとは思います。

コマンドの実行結果を取得したい

bash なら

ret=`ls -lt `
echo $ret

とするだけなのですが。

python のsubprocess モジュールで実行するには、check_outputを使う

import subprocess
cmd = "ls -lt "
ret  =  subprocess.check_output( cmd.split(" ") )
print ret

コマンドの実行終了をチャント待つには

check_callを使う。

import subprocess
cmd = "ls -lt "
ret  =  subprocess.check_call( cmd.split(" ") )
print ret == 0

実行結果が正常終了なら0がかえってくる。

call でパイプラインをチャント渡すには。

subprocessにシェル経由実行するように渡すと、パイプラインでもシッカリ処理してもらえる。

import subprcoess
cmd = "ls -lt | head -n 5"
subprocess.call( cmd , shell=True )

非シェル経由で呼び出すには。

subprocess のPIPEとstdoutを使うと実現できる。

cmd1 = "ls -lt "
cmd2 = "head -n 5 "
p1 = subprocess.Popen(cmd1.strip().split(" "), stdout=subprocess.PIPE)
p2 = subprocess.Popen(cmd2.strip().split(" "), stdin=p1.stdout)
p1.stdout.close()
output = p2.communicate()[0]

非シェル経由の場合コマンドの実行をパイプ経由で送るには、ちょっと面倒臭い手続きが必要になる。

最後の2行はおまじない

p1.stdout.close()
output = p2.communicate()[0]

これを書くことで、Python の実行を CTRL+C をで止めた時に、サブプロセスがちゃんと終了してくれる。書かないとゴーストで残る。時間のかかる処理をしていると怖い。

ただ、シェル経由しない実行は、コマンド・インジェクション対策などで使えるので重要なテクニックになると思う。

getoutput より、check_output

getoutput の紹介記事が増えているが、シェル経由なので注意が必要

# getoutput より、check_output
cmds = shlex.split('which ls;')
ret= subprocess.check_output(cmds)

上記の例は、check_outputであればエラーだが、getoutputでは実行される。

getoutput は、プロセス起動せずに、シェルに文字列を渡すので、インジェクションを引き起こしやすい。なので、固定文字以外に使うべきではない。変数を入れてはいけない。変数をエスケープするくらいなら、はじめからgetoutputを避けるべき。

2016-06-28追記

spawn という名前はイメージしやすかったけどなくなりました。

2018-09-18

追記

2024-08-08 追記

getoutput の使い分けがかなり重要だと思うので追加。

参考資料

15.1. os --- 雑多なオペレーティングシステムインタフェース — Python 2.7.18 ドキュメント

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