[Python2.7] subprocess の使い方まとめ

# 概要
subprocessを使う機会が結構多いので、わからなくて調べた点をまとめます。
調査時の自分のユースケースとして`Python 2.7`環境が前提だったため、本記事は`Python 2.7`向けになります。

基本的には以下の公式ドキュメントを参照して下さい。
[17.1. subprocess — サブプロセス管理 — Python 2.7.x ドキュメント](http://docs.python.jp/2.7/library/subprocess.html#module-subprocess)

特にPython3.5以上の場合は汎用の`run()`コマンドが追加されていたりと使用方法が異なっているため、
そのまま転用せず公式を参照して下さい。
[17.5. subprocess — サブプロセス管理 — Python 3.5.2 ドキュメント](http://docs.python.jp/3.5/library/subprocess.html#frequently-used-arguments)

# インポート指定

```py:import
import subprocess
```

# 基本的なAPI
## subprocess.call
・実行時の標準出力/標準エラーは実行されたタイミングで出力される
・正常時も異常時もリターンコードを返す

```py:実行サンプル
import subprocess

print '!!! start'

cmd = 'echo aaa'
retcode = subprocess.call(cmd.split())
print retcode

cmd = 'false'
retcode = subprocess.call(cmd.split())
print retcode

print '!!! end'
```
```console:出力
aaa
!!! start
0
1
!!! end
```

## subprocess.check_call
・実行時の標準出力/標準エラーは実行されたタイミングで出力される
・正常時にはリターンコード0を返す
・異常時には [`subprocess.CalledProcessError` 例外](http://docs.python.jp/2.7/library/subprocess.html#subprocess.CalledProcessError)を送出する

```py:実行サンプル
import subprocess

print '!!! start'

cmd = 'echo aaa'
retcode = subprocess.check_call(cmd.split())
print retcode

cmd = 'false'
try:
    retcode = subprocess.check_call(cmd.split())
except subprocess.CalledProcessError as e:
    print e.returncode
    print e.cmd
    print e.output

print '!!! end'
```
```console:出力
aaa
!!! start
0
1
['false']
None
!!! end
```

## subprocess.check_output
・実行時の標準出力/標準エラーは実行されたタイミングで出力される
・正常時には標準出力を返す
・異常時には [`subprocess.CalledProcessError` 例外](http://docs.python.jp/2.7/library/subprocess.html#subprocess.CalledProcessError)を送出する

```py:実行サンプル
import subprocess

print '!!! start'

cmd = 'echo aaa'
d = subprocess.check_output(cmd.split())
print d,

cmd = 'unexist_command'
try:
    d = subprocess.check_output(
            cmd.split(),
            stderr=subprocess.STDOUT,
            shell=True)
    print d,
except subprocess.CalledProcessError as e:
    print e.returncode
    print e.cmd
    print e.output,

print '!!! end'
```
```console:出力
!!! start
aaa
127
['unexist_command']
/bin/sh: unexist_command: command not found
!!! end
```

# `subprocess.check_output` 使用時に標準エラーを取得する

以下のように、`stderr=subprocess.STDOUT` を指定する。

```py:使用例
    d = subprocess.check_output(
            cmd.split(),
            stderr=subprocess.STDOUT,
            shell=True)
```

# subprocessの実行ディレクトリを変更する

## `cwd` オプションの使用

```py:実行ディレクトリの指定
subprocess.call(cmd, cwd=workdir)
```

実行例を以下に示す。

```py:実行サンプル
import subprocess

d1 = subprocess.check_output(['pwd'])                   # 初期位置 /tmp
d2 = subprocess.check_output(['pwd'], cwd='/tmp/hoge')  # 絶対パスでの確認
d3 = subprocess.check_output(['pwd'])                   # 他の移動の影響は受けない
d4 = subprocess.check_output(['pwd'], cwd='./hoge')     # 相対パスでの確認

print 'case 1:'
print d1,
print d2,

print 'case 2:'
print d3,
print d4,
```

```console:実行例
case 1:
/tmp
/tmp/hoge
case 2:
/tmp
/tmp/hoge
```

[python subprocess changing directory - Stack Overflow](http://stackoverflow.com/questions/21406887/python-subprocess-changing-directory)

# 注意点
実行コマンドを外部から取り込む場合、特に `shell=True` を指定している場合は、コマンド注入攻撃をされる場合が想定されるため、セキュリティには十分注意すること

## 補足：コマンド注入攻撃 / Shell Injection
[IPA ISEC　セキュア・プログラミング講座：Webアプリケーション編　第6章 入力対策：コマンド注入攻撃対策](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/501.html)
[Code injection - Wikipedia](https://en.wikipedia.org/wiki/Code_injection#Shell_injection)

# 不明な動作
・`check_output`で`shell=True`を指定した場合に、標準出力/標準エラーが取得できない場合がある。(空文字列が返る)

# 参考
[Python の subprocess で出力を受け取るときは communicate() を使おう - Qiita](http://qiita.com/mokemokechicken/items/a84b0aa96b94d1931f08)
[pythonでコマンド実行するには - それマグで！](http://takuya-1st.hatenablog.jp/entry/2014/08/23/022031)