目次

前のトピックへ

17. プロセス間通信とネットワーク

次のトピックへ

17.2. socket — 低レベルネットワークインターフェース

このページ

17.1. subprocess — サブプロセス管理

バージョン 2.4 で追加.

subprocess モジュールは、新しくプロセスを開始したり、それらの標準入出力/エラー出力に対してパイプで接続したり、それらの終了ステータスを取得したりします。このモジュールは以下のような古いいくつかのモジュールを置き換えることを目的としています:

os.system
os.spawn*
os.popen*
popen2.*
commands.*

これらのモジュールや関数の代わりに、 subprocess モジュールをどのように使うかについては以下の節で説明します。

参考

PEP 324 – PEP proposing the subprocess module

17.1.1. subprocess モジュールを使う

このモジュールでは Popen と呼ばれるクラスを定義しています:

class subprocess.Popen(args, bufsize=0, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0)

各引数の説明は以下のとおりです:

args は文字列か、あるいはプログラムへの引数のシーケンスです。実行するプログラムは通常 args シーケンスあるいは文字列の最初の要素ですが、 executable 引数を使うことにより明示的に指定することもできます。 executable が与えられると、その引数シーケンスの最初の要素は、多くのプログラムではコマンド名として扱われます。ただ、コマンド名は実際に実行される名前とは違う場合があります。 Unix では、コマンド名は ps のようなユーティリティプログラムに表示されます。

Unix で shell=False の場合 (デフォルト): この場合、 Popen クラスは子プログラムを実行するのに os.execvp() を使います。 文字列が引数として与えられた場合、実行されるプログラムの名前かパスとして使われます;ただし、プログラムは引数無しの場合のみ動作します。

ノート

args を正しくトークン化するには、 shlex.split() が便利です。このメソッドは特に複雑な状況で活躍します。

>>> import shlex, subprocess
>>> command_line = raw_input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print args
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "ech '$MONEY"]
>>> p = subprocess.Popen(args) # Success!

シェルの中で (-inputeggs.txt のように) スペースで区切られたオプションと引数はリストの別の要素として区切られていること、シェルの中で (上にあるようなスペースを含むファイル名や echo コマンドのように) クォーティングかバックスラッシュエスケープが必要なものは単一のリスト要素にされていることに注目してください。

Unix で shell=True の場合: args が文字列の場合、シェルを介して実行されるコマンドライン文字列を指定します。文字列は厳密にシェルプロンプトで打つ形式と一致しなければなりません。例えば、文字列の中にスペースを含むファイル名がある場合、はクォーティングかバックスラッシュエスケープが必要です。 args が文字列の場合には最初の要素はコマンド名を表わす文字列としてそして残りの要素は続く引数としてシェルに渡されます。これは、以下の Popen と等価ということです。

Popen(['/bin/sh', '-c', args[0], args[1], ...])

Windows の場合: Popen クラスは子プログラムを実行するのに文字列の扱える CreateProcess() を使います。 args がシーケンスの場合、これは list2cmdline() メソッドをつかってコマンドライン文字列に変換されます。注意: すべての MS Windows アプリケーションがコマンドライン引数を同じやりかたで解釈するとは限りません。 list2cmdline() は MS C ランタイムと同じやりかたで文字列を解釈するアプリケーション用に設計されています。

bufsize は、もしこれが与えられた場合、ビルトインの open() 関数の該当する引数と同じ意味をもちます: 0 はバッファされないことを意味し、 1 は行ごとにバッファされることを、それ以外の正の値は (ほぼ) その大きさのバッファが使われることを意味します。負の bufsize はシステムのデフォルト値が使われることを意味し、通常これはバッファがすべて有効となります。 bufsize のデフォルト値は 0 (バッファされない) です。

ノート

パフォーマンス上の問題がある場合、 bufsize を -1 か十分大きな正の値 (例えば 4096) に設定し、バッファを有効にすることを勧めます。

executable 引数には実行するプログラムを指定します。これはほとんど必要ありません: ふつう、実行するプログラムは args 引数で指定されるからです。 shell=True の場合、 executable 引数は使用するシェルを指定します。 Unix では、デフォルトのシェルは /bin/sh です。Windows では、デフォルトのシェルは COMSPEC 環境変数で指定されます。 Windows で shell=True を有効にする必要があるのは dircopy などのシェル組み込みのコマンドを使いたい場合だけです。バッチファイルを実行するときも、コンソールベースで起動するときも、 shell=True にする必要はありません。

stdin, stdout および stderr には、実行するプログラムの標準入力、標準出力、および標準エラー出力のファイルハンドルをそれぞれ指定します。とりうる値は PIPE 、既存のファイル記述子 (正の整数)、既存のファイルオブジェクト、そして None です。 PIPE を指定すると新しいパイプが子プロセスに向けて作られます。 None を指定するとリダイレクトは起こりません。子プロセスのファイルハンドルはすべて親から受け継がれます。加えて、 stderrSTDOUT にすると、アプリケーションの stderr からの出力は stdout と同じファイルハンドルに出力されます。

preexec_fn に callable オブジェクトが指定されている場合、このオブジェクトは子プロセスが起動されてから、プログラムが exec される直前に呼ばれます。(Unixのみ) もしくは、Windowsで close_fds が真の場合、すべてのファイルハンドルは子プロセスに引き継がれません。 Windowsの場合、 close_fds を真にしながら、 stdin, stdout, stderr を利用して標準ハンドルをリダイレクトすることはできません。

close_fds が真の場合、子プロセスが実行される前に 01 および 2 をのぞくすべてのファイル記述子が閉じられます。(Unixのみ)

shellTrue の場合、指定されたコマンドはシェルを介して実行されます。

cwdNone 以外の場合、子プロセスのカレントディレクトリが実行される前に cwd に変更されます。このディレクトリは実行ファイルを探す段階では考慮されませんので、プログラムのパスを cwd に対する相対パスで指定することはできない、ということに注意してください。

envNone 以外の場合、これは新しいプロセスでの環境変数を定義します。デフォルトでは、子プロセスは現在のプロセスの環境変数を引き継ぎます。

ノート

env を特定の値として与える場合、プログラムを実行するのに必要な変数全てを与えなければなりません。 Windows で side-by-side assembly を実行するためには、 env は正しい SystemRoot含まなければいけません

universal_newlinesTrue の場合、 stdout および stderr のファイルオブジェクトはテキストファイルとして open されますが、行の終端は Unix形式の行末 '\n' か、古い Macintosh 形式の行末 '\r' か、あるいは Windows 形式の行末 '\r\n' のいずれも許されます。これらすべての外部表現は Python プログラムには '\n' として認識されます。

ノート

この機能は Python に universal newline がサポートされている場合 (デフォルト) にのみ有効です。また、 stdout, stdin および stderr のファイルオブジェクトの newlines 属性は communicate() メソッドでは更新されません。

startupinfo および creationflags が与えられた場合、これらは内部で呼びだされる CreateProcess() 関数に渡されます。これらはメインウインドウの形状や新しいプロセスの優先度などを指定することができます。 (Windows のみ)

subprocess.PIPE

Popenstdin, stdout, stderr 引数に渡して、標準ストリームに対するパイプを開くことを指定するための特別な値.

subprocess.STDOUT

Popenstderr 引数に渡して、標準エラーが標準出力と同じハンドルに出力されるように指定するための特別な値.

17.1.1.1. 便利な関数

このモジュールは二つのショートカット関数も定義しています:

subprocess.call(*popenargs, **kwargs)

コマンドを指定された引数で実行し、そのコマンドが完了するのを待って、 returncode 属性を返します。

この引数は Popen コンストラクタの引数と同じです。使用例:

>>> retcode = call(["ls", "-l"])
subprocess.check_call(*popenargs, **kwargs)

コマンドを引数付きで実行します。コマンドが完了するのを待ちます。終了コードがゼロならば終わりますが、そうでなければ CalledProcessError 例外を送出します。 CalledProcessError オブジェクトにはリターンコードが returncode 属性として収められています。

引数は Popen コンストラクタと一緒です。使用例:

>>> subprocess.check_call(["ls", "-l"])
0

バージョン 2.5 で追加.

17.1.1.2. 例外

子プロセス内で raise した例外は、新しいプログラムが実行される前であれば、親プロセスでも raise されます。さらに、この例外オブジェクトには child_traceback という属性が追加されており、これには子プロセスの視点からの traceback 情報が格納されています。

もっとも一般的に起こる例外は OSError です。これは、たとえば存在しないファイルを実行しようとしたときなどに発生します。アプリケーションは OSError 例外にはあらかじめ準備しておく必要があります。

不適当な引数で Popen が呼ばれた場合は、 ValueError が発生します。

check_call() はもし呼び出されたプロセスがゼロでないリターンコードを返したならば CalledProcessError を送出します。

17.1.1.3. セキュリティ

ほかの popen 関数とは異なり、この実装は決して暗黙のうちに /bin/sh を実行しません。これはシェルのメタ文字をふくむすべての文字が安全に子プロセスに渡されるということを意味しています。

17.1.2. Popen オブジェクト

Popen クラスのインスタンスには、以下のようなメソッドがあります:

Popen.poll()

子プロセスが終了しているかどうかを検査します。 returncode 属性を設定し、返します。

Popen.wait()

子プロセスが終了するまで待ちます。 returncode 属性を設定し、返します。

警告

子プロセスが stdout もしくは stderr パイプに対してブロックするまで出力し、 OSのパイプバッファが送信可能になるまで待つ場合、このメソッドを呼ぶとデッドロックします。これを避けるために、 communicate() を利用してください。

Popen.communicate(input=None)

プロセスと通信します: end-of-file に到達するまでデータを stdin に送信し、stdout および stderr からデータを受信します。プロセスが終了するまで待ちます。オプション引数 input には子プロセスに送られる文字列か、あるいはデータを送らない場合は None を指定します。

communicate() はタプル (stdoutdata, stderrdata) を返します。

子プロセスの標準入力にデータを送りたい場合は、 Popen オブジェクトを stdin=PIPE と指定して作成しなければなりません。同じく、戻り値のタプルから None ではない値を取得するためには、 stdout=PIPE かつ/または stderr=PIPE を指定しなければなりません。

ノート

受信したデータはメモリ中にバッファされます。そのため、返されるデータが大きいかあるいは制限がないような場合はこのメソッドを使うべきではありません。

Popen.send_signal(signal)

signal シグナルを子プロセスに送ります。

ノート

Windows では SIGTERM だけがサポートされています。これは terminate() のエイリアスです。

バージョン 2.6 で追加.

Popen.terminate()

子プロセスを止めます。 Posix OSでは、このメソッドは SIGTERM シグナルを子プロセスに送ります。 Windows では、 Win32 API の TerminateProcess() 関数を利用して子プロセスを止めます。

バージョン 2.6 で追加.

Popen.kill()

子プロセスを殺します。 Posix OS では SIGKILL シグナルを子プロセスに送ります。 Windows では、 kill()terminate() のエイリアスです。

バージョン 2.6 で追加.

以下の属性も利用できます:

警告

stdin.write(), stdout.read(), stderr.read() を利用すると、別のパイプのOSパイプバッファがいっぱいになってデッドロックする恐れがあります。これを避けるためには communicate() を利用してください。

Popen.stdin

stdin 引数が PIPE の場合、この属性には子プロセスの入力に使われるファイルオブジェクトになります。そうでない場合は None です。

Popen.stdout

stdout 引数が PIPE の場合、この属性には子プロセスの出力に使われるファイルオブジェクトになります。そうでない場合は None です。

Popen.stderr

stderr 引数が PIPE の場合、この属性には子プロセスのエラー出力に使われるファイルオブジェクトになります。そうでない場合は None です。

Popen.pid

子プロセスのプロセス ID が入ります。

shell 引数を True にセットした場合は、生成されたシェルのプロセス ID になります。

Popen.returncode

poll()wait() (か、間接的に communicate() )から設定された、子プロセスの終了ステータスが入ります。 None はまだその子プロセスが終了していないことを示します。

負の値 -N は子プロセスがシグナル N により中止させられたことを示します (Unix のみ)。

17.1.3. 古い関数を subprocess モジュールで置き換える

以下、この節では、”a ==> b” と書かれているものは a の代替として b が使えるということを表します。

ノート

この節で紹介されている関数はすべて、実行するプログラムが見つからないときは (いくぶん) 静かに終了します。このモジュールは OSError 例外を発生させます。

以下の例では、 subprocess モジュールは “from subprocess import *” でインポートされたと仮定しています。

17.1.3.1. /bin/sh シェルのバッククォートを置き換える

output=`mycmd myarg`
==>
output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]

17.1.3.2. シェルのパイプラインを置き換える

output=`dmesg | grep hda`
==>
p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
output = p2.communicate()[0]

17.1.3.3. os.system() を置き換える

sts = os.system("mycmd" + " myarg")
==>
p = Popen("mycmd" + " myarg", shell=True)
sts = os.waitpid(p.pid, 0)[1]

注意:

  • このプログラムは普通シェル経由で呼び出す必要はありません。
  • 終了状態を見るよりも returncode 属性を見るほうが簡単です。

より現実的な例ではこうなるでしょう:

try:
    retcode = call("mycmd" + " myarg", shell=True)
    if retcode < 0:
        print >>sys.stderr, "子プロセスがシグナルによって中止されました", -retcode
    else:
        print >>sys.stderr, "子プロセスが終了コードを返しました", retcode
except OSError, e:
    print >>sys.stderr, "実行に失敗しました:", e

17.1.3.4. os.spawn 関数群を置き換える

P_NOWAIT の例:

pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
==>
pid = Popen(["/bin/mycmd", "myarg"]).pid

P_WAIT の例:

retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
==>
retcode = call(["/bin/mycmd", "myarg"])

シーケンスを使った例:

os.spawnvp(os.P_NOWAIT, path, args)
==>
Popen([path] + args[1:])

環境変数を使った例:

os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
==>
Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})

17.1.3.5. os.popen(), os.popen2(), os.popen3() を置き換える

pipe = os.popen("cmd", 'r', bufsize)
==>
pipe = Popen("cmd", shell=True, bufsize=bufsize, stdout=PIPE).stdout
pipe = os.popen("cmd", 'w', bufsize)
==>
pipe = Popen("cmd", shell=True, bufsize=bufsize, stdin=PIPE).stdin
(child_stdin, child_stdout) = os.popen2("cmd", mode, bufsize)
==>
p = Popen("cmd", shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdin, child_stdout) = (p.stdin, p.stdout)
(child_stdin,
 child_stdout,
 child_stderr) = os.popen3("cmd", mode, bufsize)
==>
p = Popen("cmd", shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
(child_stdin,
 child_stdout,
 child_stderr) = (p.stdin, p.stdout, p.stderr)
(child_stdin, child_stdout_and_stderr) = os.popen4("cmd", mode,
                                                   bufsize)
==>
p = Popen("cmd", shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)

Unix では、 os.popen2、os.popen3、os.popen4 は実行するコマンドとしてシーケンスも受け入れます。どちらにせよ、引数はシェルの干渉を受けることなく直接渡されます。この使い方は以下のように置き換えられます。

(child_stdin, child_stdout) = os.popen2(["/bin/ls", "-l"], mode,
                                        bufsize)
==>
p = Popen(["/bin/ls", "-l"], bufsize=bufsize,
          stdin=PIPE, stdout=PIPE)
(child_stdin, child_stdout) = (p.stdin, p.stdout)

終了コードハンドリングは以下のように解釈します。

pipe = os.popen("cmd", 'w')
...
rc = pipe.close()
if rc is not None and rc >> 8:
    print "There were some errors"
==>
process = Popen("cmd", 'w', shell=True, stdin=PIPE)
...
process.stdin.close()
if process.wait() != 0:
    print "There were some errors"

17.1.3.6. popen2 モジュールの関数群を置き換える

(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
==>
p = Popen(["somestring"], shell=True, bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)

Unix では、 popen2 は実行するコマンドとしてシーケンスも受け入れます。どちらにせよ、引数はシェルの干渉を受けることなく、直接渡されます。この使い方は、以下のように置き換えられます。

(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize,
                                            mode)
==>
p = Popen(["mycmd", "myarg"], bufsize=bufsize,
          stdin=PIPE, stdout=PIPE, close_fds=True)
(child_stdout, child_stdin) = (p.stdout, p.stdin)

popen2.Popen3 および popen2.Popen4 は基本的には subprocess.Popen と同様です。ただし、違う点は:

  • Popen は実行できなかった場合に例外を発生させます。
  • capturestderr 引数は stderr 引数に代わりました。
  • stdin=PIPE および stdout=PIPE を指定する必要があります。
  • popen2 はデフォルトですべてのファイル記述子を閉じますが、 Popen では明示的に close_fds=True を指定する必要があります。