subprocessについて調べたメモ

More than 1 year has passed since last update.


非推奨コマンド


  • 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:エンコード時のエラー処理