Python は外部プロセスを実行し、オペレーティングシステムと対話するためのいくつかのオプションを提供します。
しかし、その方法は Python 2 と 3 で異なっています。
Python 2 では os
モジュールにいくつかのメソッドがありましたが、現在では非推奨となり、Python 3 で優先される subprocess
モジュールに置き換えられています。
この記事では、様々な os
と subprocess
のメソッドについて、その使い方、それぞれの違い、どのバージョンの Python で使うべきか、さらに古いコマンドを新しいコマンドに変換する方法について説明します。
この記事が終わる頃には、Pythonのコードから外部コマンドを呼び出す方法と、そのためにどのメソッドを使うべきかについて、より良く理解できるようになっていることを願っています。
まず最初に、古い os.popen*
メソッドについて説明します。
The os.popen* Methods
osモジュールは 4 つの異なるメソッドを提供し、オペレーティングシステムと対話したり (コマンドラインと同じように) 他のコマンドへのパイプを作成したりすることができます。
これらのメソッドについて説明します。
popen、popen2
、popen3
、popen4
で、いずれも次のセクションで説明します。
これらのメソッドの目的は、Pythonのコードから他のプログラムを呼び出すことができるようになることです。
これは、コンパイルされたC++プログラムのような別の実行ファイルを呼び出したり、 ls
や mkdir
のようなシェルコマンドを呼び出したりすることができます。
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行目のコードでは in
と out
という2つの変数を定義しています。
最後の行では、出力ファイル out
を読み込んでコンソールに出力しています。
os.popen3
この方法は、前のものと非常によく似ています。
しかし、コマンドの出力が stdin, stdout, stderr の3つのファイルのセットである点が異なります。
構文は次のとおりです。
os.popen3(cmd[, mode[, bufsize]])
ここで、引数 cmd
、mode
および 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の dir
と sort
コマンドを組み合わせたものです。
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のコマンドシェルからこれを実行すると、以下のようになります。
> 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