Python の os とサブプロセスの Popen コマンド

Python は外部プロセスを実行し、オペレーティングシステムと対話するためのいくつかのオプションを提供します。

しかし、その方法は Python 2 と 3 で異なっています。

Python 2 では os モジュールにいくつかのメソッドがありましたが、現在では非推奨となり、Python 3 で優先される subprocess モジュールに置き換えられています。

この記事では、様々な ossubprocess のメソッドについて、その使い方、それぞれの違い、どのバージョンの Python で使うべきか、さらに古いコマンドを新しいコマンドに変換する方法について説明します。

この記事が終わる頃には、Pythonのコードから外部コマンドを呼び出す方法と、そのためにどのメソッドを使うべきかについて、より良く理解できるようになっていることを願っています。

まず最初に、古い os.popen* メソッドについて説明します。

The os.popen* Methods

osモジュールは 4 つの異なるメソッドを提供し、オペレーティングシステムと対話したり (コマンドラインと同じように) 他のコマンドへのパイプを作成したりすることができます。

これらのメソッドについて説明します。

popen、popen2popen3popen4 で、いずれも次のセクションで説明します。

これらのメソッドの目的は、Pythonのコードから他のプログラムを呼び出すことができるようになることです。

これは、コンパイルされたC++プログラムのような別の実行ファイルを呼び出したり、 lsmkdir のようなシェルコマンドを呼び出したりすることができます。

os.popen

os.popen` メソッドはコマンドからパイプを開きます。

このパイプにより、コマンドはその出力を別のコマンドに送信することができます。

出力は、他のプログラムからアクセスできるオープンファイルです。

シンタックスは次のとおりです。

os.popen(command[, mode[, bufsize]])


ここで command パラメータは実行するコマンドで、その出力はオープンファイルを介して利用できるようになります。

引数 mode は、この出力ファイルが読み込み可能 (‘r’) か書き込み可能 (‘w’) かを定義する。

mode` に ‘b’ を追加すると、ファイルをバイナリモードで開くことができる。

したがって、たとえば “rb” と入力すると、読み取り可能なバイナリファイルオブジェクトが生成されます。

実行されたコマンドの終了コードを取得するためには、ファイルオブジェクトの close() メソッドを使用する必要があります。

bufsizeパラメータはpopen` にどれだけのデータをバッファリングするかを指示し、以下のいずれかの値をとります。

  • 0 = バッファ無し (デフォルト値)
  • 1 = ラインバッファリング
  • N = おおよそのバッファサイズ、N > 0 のとき、デフォルト値、N < 0 のとき

このメソッドは Unix と Windows プラットフォームで利用可能で、Python バージョン 2.6 以降は非推奨となっています。

現在このメソッドを使用していて、Python 3 のバージョンに切り替えたい場合は、以下が同等の Python 3 用の subprocess バージョンになります。

| メソッド | に置き換わる

| 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 | を実行します。

以下のコードは os.popen メソッドの使い方の例を示しています。

import os


p = os.popen('ls -la')
print(p.read())


上のコードはオペレーティングシステムに、カレントディレクトリにあるすべてのファイルをリストアップするように要求します。

このメソッドの出力は p に格納され、オープンされたファイルが読み込まれ、コードの最後の行で表示されます。

このコード(カレントディレクトリのコンテキスト)の結果は次のようになります。

$ python popen_test.py 
total 32
drwxr-xr-x   7 scott  staff  238 Nov  9 09:13 .
drwxr-xr-x  29 scott  staff  986 Nov  9 09:08 ..
-rw-r--r--   1 scott  staff   52 Nov  9 09:13 popen2_test.py
-rw-r--r--   1 scott  staff   55 Nov  9 09:14 popen3_test.py
-rw-r--r--   1 scott  staff   53 Nov  9 09:14 popen4_test.py
-rw-r--r--   1 scott  staff   49 Nov  9 09:13 popen_test.py
-rw-r--r--   1 scott  staff    0 Nov  9 09:13 subprocess_popen_test.py


os.popen2

このメソッドは前のメソッドと非常によく似ています。

主な違いは、このメソッドが出力するものです。

この場合、2つのファイルオブジェクト、1つは標準入力用、もう1つは標準出力用のファイルを返します。

構文は次のとおりです。

popen2(cmd[, mode[, bufsize]])


これらの引数は、前のメソッド os.popen と同じ意味です。

popen2メソッドは、Unix と Windows の両方のプラットフォームで利用可能です。

しかし、Python 2 にしかありません。

繰り返しになりますが、代わりにsubprocess` バージョン(以下に詳細を示します)を使用したい場合は、以下を代わりに使用してください。

| メソッド | に置き換えます。

| (child_stdin, child_stdout) = os.popen2(‘cmd’, mode, bufsize) | (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)
|

以下に、このメソッドの使用例を示す。

import os


in, out = os.popen2('ls -la')
print(out.read())


このコードは、上記の最初のコード出力に示されるのと同じ結果を生成します。

ここでの違いは、popen2メソッドの出力が2つのファイルから構成されていることです。

したがって、2行目のコードでは inout という2つの変数を定義しています。

最後の行では、出力ファイル out を読み込んでコンソールに出力しています。

os.popen3

この方法は、前のものと非常によく似ています。

しかし、コマンドの出力が stdin, stdout, stderr の3つのファイルのセットである点が異なります。

構文は次のとおりです。

os.popen3(cmd[, mode[, bufsize]])


ここで、引数 cmdmode および bufsize は、前のメソッドと同じ仕様である。

このメソッドは、Unix および Windows プラットフォームで利用可能です。

なお、このメソッドは非推奨となっており、Pythonのドキュメントでは、以下のように popen3 メソッドを置き換えるようにアドバイスしています。

メソッド に置き換わる
(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)

これまでの例と同様に、以下のコードを実行すると、最初の例で見たのと同じ結果になる。

import os


in, out, err = os.popen3('ls -la')
print(out.read())


ただし、この場合、stdin, stdout, stderrの3つのファイルを定義する必要があります。

ls -laコマンドによるファイルの一覧はout` ファイルに保存されます。

os.popen4

おそらく推測されるように、os.popen4 メソッドは前のメソッドと似ています。

しかし、この場合、2つのファイルだけを返します。

1つは標準入力用、もう1つは標準出力と標準エラー用です。

このメソッドは Unix と Windows プラットフォームで利用可能で、(なんと!) バージョン 2.6 からは非推奨になっています。

これを対応する subprocess Popen 呼び出しに置き換えるには、以下のようにします。

| メソッド(Method) | 置換されるもの(Replaceed by |)
| — | — |
|
(child_stdin, child_stdout ◇ ◇ and ◇) = 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, alt=Stdout) = (p.stdin, p.stdout)
|

次のコードは、上記の最初のコード出力に示すように、前の例と同じ結果を生成する。

import os


in, out = os.popen4('ls -la')
print(we.read())


上のコードからわかるように、このメソッドは popen2 と非常によく似ています。

しかし、プログラム中の out ファイルには、stdout と stderr の両方のストリームの結果を合わせたものが表示されます。

相違点のまとめ

異なる popen* コマンドの違いは、すべてその出力に関係しており、以下の表にまとめられています。

| メソッド|引数

| popen | stdout
| popen2 | stdin, stdout | です。

| popen3 | stdin, stdout, stderr | です。

| popen4 | stdin, stdout, stderr | です。

また、popen2, popen3, popen4 は Python 2 でのみ利用可能で、Python 3 では利用できません。

Python 3 では popen メソッドが利用可能ですが、代わりに subprocess モジュールを使用することが推奨されます。

susbprocess.Popen メソッド

subprocess モジュールは、あまり効率的でないと考えられていた os モジュールのいくつかのメソッドを置き換えることを意図して作成されました。

このモジュールの中には、新しい Popen クラスがあります。

Python のドキュメントでは、 subprocess.call のような他のメソッドでは対応できない高度なケースで Popen を使用することを勧めています。

このメソッドは、プログラムを子プロセスとして実行することを可能にします。

これはオペレーティングシステムによって別のプロセスとして実行されるため、結果はプラットフォームに依存する。

利用可能なパラメータは以下の通りです。

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)


Popenの大きな違いは、単なるメソッドではなくクラスであることである。

したがって、subprocess.Popenを呼び出すとき、実際にはPopen` クラスのコンストラクタを呼び出していることになる。

コンストラクタには非常に多くの引数があります。

最も重要なのは args で、これは実行したいプロセスのコマンドを含んでいる。

これは、一連のパラメータ (配列) として、または単一のコマンド文字列として指定することができる。

2つ目の引数は shell で、デフォルトでは False になっています。

Unix では、 ls -la のようにシェルに属するコマンドを実行する必要がある場合、 shell=True を設定する必要があります。

例えば、以下のコードはUnixのコマンド ls -la をシェル経由で呼び出します。

import subprocess
subprocess.Popen('ls -la', shell=True)


結果は以下の出力で見ることができます。

$ python subprocess_popen_test.py 
total 40
drwxr-xr-x   7 scott  staff  238 Nov  9 09:13 .
drwxr-xr-x  29 scott  staff  986 Nov  9 09:08 ..
-rw-r--r--   1 scott  staff   52 Nov  9 09:13 popen2_test.py
-rw-r--r--   1 scott  staff   55 Nov  9 09:14 popen3_test.py
-rw-r--r--   1 scott  staff   53 Nov  9 09:14 popen4_test.py
-rw-r--r--   1 scott  staff   49 Nov  9 09:13 popen_test.py
-rw-r--r--   1 scott  staff   56 Nov  9 09:16 subprocess_popen_test.py


次の例はWindowsマシンからですが、shellパラメータを使うことの違いをもっと簡単に見ることができます。

ここでは、Microsoft Excelをシェルから、つまり実行可能なプログラムとして開いています。

次のコードは、シェルからExcelを開きます(shell=Trueを指定する必要があることに注意してください)。

import subprocess
subprocess.Popen("start excel", shell=True)


しかし、Excelの実行ファイルを呼び出しても同じ結果を得ることができます。

この場合、シェルを使用しないので、デフォルト値(False)のままにしておきます。

import subprocess
subprocess.Popen("C:Program Files (x86)Microsoft OfficeOffice15excel.exe")


さらに、Popen クラスをインスタンス化すると、いくつかの便利なメソッドにアクセスできるようになります。

| メソッド
| — | — |
| Popen.poll() | 子プロセスが終了したかどうかをチェックします。

|
| Popen.wait() | 子プロセスが終了するのを待ちます。

|
| Popen.communicate() | プロセスと対話できるようにする。

|
| Popen.send_signal() | 子プロセスにシグナルを送信します。

|
| Popen.terminate() | 子プロセスを停止します。

|
| Popen.kill() | 子プロセスを終了します。

完全なリストはサブプロセスのドキュメントで見ることができます。

ここで最もよく使われるメソッドは communicate です。

このメソッドによって、標準入力からデータを読み込んだり、標準出力にデータを送ったりすることができます。

このメソッドは (stdoutdata, stderrdata) として定義されたタプルを返します。

例えば、次のコードはWindowsの dirsort コマンドを組み合わせたものです。

import subprocess


p1 = subprocess.Popen('dir', shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p2 = subprocess.Popen('sort /R', shell=True, stdin=p1.stdout)


p1.stdout.close()
out, err = p2.communicate()


両方のコマンドを組み合わせるために、2つのサブプロセスを作成します。

1つは dir コマンド用、もう1つは sort コマンド用のサブプロセスです。

逆順にソートしたいので、 sort の呼び出しに /R オプションを追加します。

プロセス 1 の標準出力を PIPE として定義し、プロセス 1 の出力をプロセス 2 の入力として使用できるようにします。

そして、プロセス1の標準出力を閉じて、プロセス2が入力として使えるようにする必要がある。

プロセス間の通信は communicate メソッドで行われます。

Windowsのコマンドシェルからこれを実行すると、以下のようになります。

&gt; python subprocess_pipe_test.py
11/09/2017  08:52 PM                  234 subprocess_pipe_test.py
11/09/2017  07:13 PM                   99 subprocess_pipe_test2.py
11/09/2017  07:08 PM                   66 subprocess_pipe_test3.py
11/09/2017  07:01 PM                   56 subprocess_pipe_test4.py
11/09/2017  06:48 PM     <dir>            ..
11/09/2017  06:48 PM     <dir>            .
 Volume Serial Number is 2E4E-56A3
 Volume in drive D is ECA
 Directory of D:MyPopen
               4 File(s)            455 bytes
               2 Dir(s)  18,634,326,016 bytes free


ラップアップ

以前は os メソッドが良い選択肢でしたが、現在では subprocess モジュールがより強力で効率的に使用できるメソッドをいくつか持っています。

利用できるツールの中には Popen クラスがあり、より複雑なケースで使用することができます。

このクラスには communicate メソッドも含まれており、異なるコマンドをパイプでつないでより複雑な機能を実現するのに役立ちます。

あなたは popen* メソッドを何に使いますか?また、どちらが好きですか?コメントで教えてください。

</dir

タイトルとURLをコピーしました