複数のファイルハンドルに書き込みます。ここに簡単な例があり、sys.stdout
とsys.stderr
のリダイレクトテストがあります。そのコードを実行した後
import sys
class MultiOut(object):
def __init__(self, *args):
self.handles = args
def write(self, s):
for f in self.handles:
f.write(s)
with open('q1', 'w') as f1, open('q2', 'w') as f2, open('q3', 'w') as f3:
sys.stdout = MultiOut(f1, f2)
sys.stderr = MultiOut(f3, f2)
for i, c in enumerate('abcde'):
print(c, 'out')
print(i, 'err', file=sys.stderr)
は、ここではそれらのファイルが含まれているものです:
Q1
a out
b out
c out
d out
e out
Q3
0 err
1 err
2 err
3 err
4 err
Q2
a out
0 err
b out
1 err
c out
2 err
d out
3 err
e out
4 err
FWIWあなたが好きならば、あなたも、これを行うことができます:Popen
ので残念ながら、MultiOut
のようなファイルのようなオブジェクトがPopen
で使用することはできません
sys.stdout = MultiOut(f1, f2, sys.stdout)
sys.stderr = MultiOut(f3, f2, sys.stderr)
を基礎となるOSファイルディスクリプタを介してファイルにアクセスします。つまり、OSがファイルとみなすものが必要なため、有効なを提供するPythonオブジェクトだけですメソッドは、Popen
のファイル引数に使用できます。
代わりに、Python 3のasyncio
機能を使用して、シェルコマンドを実行し、stdoutとstderrの出力を同時にコピーすることができます。
まず、以下のPythonコードをテストするために使用した単純なBashスクリプトを示します。以前のPythonの例のように、配列の内容をstdoutに、配列のインデックスをstderrにエコーします。
multitest.bsh
#!/usr/bin/env bash
a=(a b c d e)
for((i=0; i<${#a[@]}; i++))
do
echo "OUT: ${a[i]}"
echo "ERR: $i" >&2
sleep 0.01
done
出力
OUT: a
ERR: 0
OUT: b
ERR: 1
OUT: c
ERR: 2
OUT: d
ERR: 3
OUT: e
ERR: 4
そして、ここでPythonの3つのファイルのQ1とQ2にその標準出力の出力をパイプ、multitest.bsh実行されるコード、およびその標準エラー出力ですq3とq2に出力する。コードを実行した後
import asyncio
from asyncio.subprocess import PIPE
class MultiOut(object):
def __init__(self, *args):
self.handles = args
def write(self, s):
for f in self.handles:
f.write(s)
def close(self):
pass
@asyncio.coroutine
def copy_stream(stream, outfile):
""" Read from stream line by line until EOF, copying it to outfile. """
while True:
line = yield from stream.readline()
if not line:
break
outfile.write(line) # assume it doesn't block
@asyncio.coroutine
def run_and_pipe(cmd, fout, ferr):
# start process
process = yield from asyncio.create_subprocess_shell(cmd,
stdout=PIPE, stderr=PIPE, executable="/bin/bash")
# read child's stdout/stderr concurrently
try:
yield from asyncio.gather(
copy_stream(process.stdout, fout),
copy_stream(process.stderr, ferr))
except Exception:
process.kill()
raise
finally:
# wait for the process to exit
rc = yield from process.wait()
return rc
# run the event loop
loop = asyncio.get_event_loop()
with open('q1', 'wb') as f1, open('q2', 'wb') as f2, open('q3', 'wb') as f3:
fout = MultiOut(f1, f2)
ferr = MultiOut(f3, f2)
rc = loop.run_until_complete(run_and_pipe("./multitest.bsh", fout, ferr))
loop.close()
print('Return code:', rc)
は、ここではそれらのファイルが含まれているものです:
Q1
OUT: a
OUT: b
OUT: c
OUT: d
OUT: e
Q3
ERR: 0
ERR: 1
ERR: 2
ERR: 3
ERR: 4
Q2
OUT: a
ERR: 0
OUT: b
ERR: 1
OUT: c
ERR: 2
OUT: d
ERR: 3
OUT: e
ERR: 4
asyncioコードは、質問Subprocess.Popen: cloning stdout and stderr both to terminal and variablesにJ.F. Sebastian's answerから持ち上げました。ありがとう、J.F!
スケジュールされたコルーチンが利用できるようになると、ファイルにデータが書き込まれることに注意してください。正確にの場合、が発生した場合は、現在のシステム負荷によって異なります。ですから、sleep 0.01
コマンドをmultitest.bshに入れて、stdoutとstderrの行を同期させた状態にしておきます。この遅れがなければ、q2のstdoutとstderrの行は一般的にうまくインターリーブされません。その同期を達成するためのより良い方法があるかもしれませんが、私はまだasyncioプログラミングの初心者です。
いくつかの回答が参考になります:http://stackoverflow.com/questions/2996887/how-to-replicate-tee-behavior-in-python-when-using-subprocess –
あなたは私に私が使っていたものと同じです。しかし、それは複数のファイル記述子にリダイレクトする必要があります。単一のファイルだけでなく、 – Swapnil
有用ではありません。 Not working – Swapnil