この記事は古いです.別記事で更新しました.
こちら.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に対してのみ)
エラー出力の統合
subprocess.check_output([cmd],stderr=subprocess.STDOUT)
##subprocess.Popen.stdin/stdout/stderr
- 返り値はopen()と同じ
- 入力に対しては.write()や.close()などが使用可能。
- 出力に対しては.read()や.close()などが使用可能。
パイプを通した出力の取得
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)が受け取れる。
communicate()を用いた取得
subprocess.Popen([cmd], stdout=subprocess.PIPE).communicate()[0]
- また、標準入力を与える際はcommunicate(input)の引数を指定する。
- テキストファイルとして受け渡す場合、ファイルをopen()で開きread()で読取った形で与える。
communicate()を用いた入力1
subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate('Hello world!\n')[0]
communicate()を用いた入力2
subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate(open('input.txt','r').read())[0]
パイプで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:エンコード時のエラー処理