subprocessについて調べたメモ

## この記事は古いです．別記事で更新しました．

こちら．https://qiita.com/HidKamiya/items/e192a55371a2961ca8a4
ここではとりあげていない`subprocess.run()`や`subprocess.Popen()`などを中心に，より広くまとめました．

##非推奨コマンド
- os.system(cmd)　#サブシェル内コマンド実行（コマンドラインでsh -c "cmd"とするのと同様）　#終了ステータス
- 　.spawn{lvpe}(mode,path/file)　#path実行。mode: os.P_..　#l/v:固定/可変長引数、p:環境変数PATH内探索、e:現プロセス環境引継
- 　.popen(cmd,mode)　#外部プロセス（スクリプト、ソフトなど）へ通信。読み書き（mode:'r','w'）　#pipe open？
- 　.exec{lvpe}(path/file)　#プロセス置換
- commands.getstatusoutput(cmd)　#（終了ステータス、結果）
- 　　　　　.getoutput(cmd)　#結果


##概要
- subprocess.call(cmd)　#終了ステータス
- 　　　　　.check_call(cmd)　#終了ステータス but エラー時例外（CalledProcessError）
- 　　　　　.check_output(cmd)　#結果 but エラー時例外（CalledProcessError）
- 　　　　　.Popen(cmd)　#入出力時にファイル指定

##cmd中身：引数を取る場合リストかタプルにする
- cmd.strip().split(" ")　なじみ深い処理。否応なくスペースがあれば区切る。
- shlex.split(cmd)　文字列内に" "などの括りがある場合、その中のスペースで分けない。
 
"ls -l" --> ['ls', '-l']
"echo 'Hello world!'" --> ['echo', 'Hello world!']　#shlex.split(cmd)のみ


##オプション
- stdin：標準入力
- stdout：標準出力
- stderr：標準エラー出力
    - 下記の特殊値
    - 既存ファイル名、記述子 (正の整数)
    - None：リダイレクト無し
- shell：シェル経由
    - sh -c "cmd"と同様
    - シェルインジェクションの危険性から非推奨。
    - シェルスクリプトに用いるような複雑なスクリプトの実行。
    - リストではなく文字列一本で入力（リストだと初めの要素がコマンド、以降は引数扱いとなる）。
    - *や~などの特殊な文字を機能させたい場合（"ls *.txt"など）
    - パイプも可能
- timeout：タイムアウト秒数


##特殊値（Special value）
- subprocess.DEVNULL：標準入出力先をos.devnull（ビットバケツ、ブラックホール）に指定
- subprocess.PIPE：標準入出力先へのパイプ指定
- subprocess.STDOUT：標準エラー出力が標準出力と同じハンドルに出力されるよう指定（2>1&。stderrに対してのみ）

```py:エラー出力の統合
subprocess.check_output([cmd],stderr=subprocess.STDOUT)
```

##subprocess.Popen.stdin/stdout/stderr
- 返り値はopen()と同じ
- 入力に対しては.write()や.close()などが使用可能。
- 出力に対しては.read()や.close()などが使用可能。

```py:パイプを通した出力の取得
subprocess.Popen(cmd, stdout=subprocess.PIPE,shell=True).stdout.readlines()
```

##subprocess.Popen.communicate()
- subprocess.Popen.stdout.read()、subprocess.Popen.stderr.read()でも出力を読むことができる。
- が、出力サイズの制約を受けるなどで非推奨。
- .communicate()に渡すと、(stdout_data, stderr_data)が受け取れる。

```py:communicate()を用いた取得
subprocess.Popen([cmd], stdout=subprocess.PIPE).communicate()[0]
```
- また、標準入力を与える際はcommunicate(input)の引数を指定する。
- テキストファイルとして受け渡す場合、ファイルをopen()で開きread()で読取った形で与える。

```py:communicate()を用いた入力１
subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate('Hello world!\n')[0]
```

```py:communicate()を用いた入力２
subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate(open('input.txt','r').read())[0]
```



```py:パイプでshellを使わない記述
p1 = subprocess.Popen([cmd1], stdout=subprocess.PIPE) #出力先にパイプ
p2 = subprocess.Popen([cmd2], stdin=p1.stdout, stdout=subprocess.PIPE) #入力にp1の受取
p1.stdout.close()
output = p2.communicate()[0] #stdout_data取得
```







##その他オプションとデフォルト値
- bufsize=-1：入出力の(ライン)バッファー設定。
- executable=None：実行する置換プログラムの指定。
- preexec_fn=None：子プロセス実行直前のファイル呼出。　#POSIX
- close_fds=True：子プロセス実行前に 0, 1, 2 以外のファイル記述子を閉じる。　#POSIX
- cwd=None：作業ディレクトリ
- env=None：新プロセスでの環境変数を定義
- universal_newlines=False：locale.getpreferredencoding() の代わりに locale.getpreferredencoding(False) を使用
- startupinfo=None：基底の CreateProcess 関数に渡される STARTUPINFO　#Windows
- creationflags=0：CREATE_NEW_CONSOLE または CREATE_NEW_PROCESS_GROUP　#Windows
- restore_signals=True：SIG_IGN に設定したすべてのシグナルを子プロセスexec前にSIG_DFLに格納
- start_new_session=False：サブプロセス実行前に子プロセス内で setsid() システムコール作成　#POSIX
- pass_fds=()：親と子の間で開いたままにしておくファイル記述子
- encoding=None：標準入出力ファイルのエンコード名
- errors=None：エンコード時のエラー処理