Popenを使用して、その標準出力と標準エラー出力をログファイルに継続的に書き込むシェルスクリプトを呼び出しています。ログファイルを連続して(画面に)同時に出力する方法、または、シェルスクリプトがログファイルとstdoutの両方に同時に書き込むようにする方法はありますか?
私は基本的にPythonでこのようなことをしたいです:
cat file 2>&1 | tee -a logfile #"cat file" will be replaced with some script
繰り返しますが、これはstderr/stdoutを一緒にteeにパイプし、stdoutと私のログファイルの両方に書き込みます。
Pythonでstdoutとstderrをログファイルに書き込む方法を知っています。私が立ち往生しているのは、これらを画面に複製する方法です:
subprocess.Popen("cat file", Shell=True, stdout=logfile, stderr=logfile)
もちろん、私はこのようなことをすることができますが、ティーとシェルファイル記述子のリダイレクトなしでこれを行う方法はありますか?:
subprocess.Popen("cat file 2>&1 | tee -a logfile", Shell=True)
パイプを使用して、プログラムの標準出力からデータを読み取り、必要なすべての場所に書き込むことができます。
_import sys
import subprocess
logfile = open('logfile', 'w')
proc=subprocess.Popen(['cat', 'file'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in proc.stdout:
sys.stdout.write(line)
logfile.write(line)
proc.wait()
_
[〜#〜] update [〜#〜]
python 3では、_universal_newlines
_パラメーターはパイプの使用方法を制御します。 False
の場合、パイプ読み取りはbytes
オブジェクトを返し、文字列を取得するためにデコードする必要がある場合があります(たとえば、line.decode('utf-8')
)。 True
の場合、pythonがデコードを行います
バージョン3.3で変更:universal_newlinesがTrueの場合、クラスはlocale.getpreferredencoding()の代わりにエンコーディングlocale.getpreferredencoding(False)を使用します。この変更の詳細については、io.TextIOWrapperクラスを参照してください。
エミュレートするには:tee
コマンドを呼び出さずにsubprocess.call("command 2>&1 | tee -a logfile", Shell=True)
:
#!/usr/bin/env python2
from subprocess import Popen, PIPE, STDOUT
p = Popen("command", stdout=PIPE, stderr=STDOUT, bufsize=1)
with p.stdout, open('logfile', 'ab') as file:
for line in iter(p.stdout.readline, b''):
print line, #NOTE: the comma prevents duplicate newlines (softspace hack)
file.write(line)
p.wait()
バッファリングの問題を修正するには(出力が遅れる場合)、 Python:subprocess.communicate() からストリーミング入力を読み取ります。
Python 3バージョン:
#!/usr/bin/env python3
import sys
from subprocess import Popen, PIPE, STDOUT
with Popen("command", stdout=PIPE, stderr=STDOUT, bufsize=1) as p, \
open('logfile', 'ab') as file:
for line in p.stdout: # b'\n'-separated lines
sys.stdout.buffer.write(line) # pass bytes as is
file.write(line)
対話型アプリケーションの場合は、バイト単位で端末に書き込みます
このメソッドは、stdoutに到達したバイトをすぐに書き込みます。これにより、特にインタラクティブアプリケーションの場合、tee
の動作をより厳密にシミュレートできます。
main.py
#!/usr/bin/env python3
import os
import subprocess
import sys
with subprocess.Popen(sys.argv[1:], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc, \
open('logfile.txt', 'bw') as logfile:
while True:
byte = proc.stdout.read(1)
if byte:
sys.stdout.buffer.write(byte)
sys.stdout.flush()
logfile.write(byte)
# logfile.flush()
else:
break
exit_status = proc.returncode
sleep.py
#!/usr/bin/env python3
import sys
import time
for i in range(10):
print(i)
sys.stdout.flush()
time.sleep(1)
最初に、非対話式の健全性チェックを実行できます。
./main.py ./sleep.py
そして、リアルタイムで標準出力にカウントされることがわかります。
次に、対話型テストの場合、次を実行できます。
./main.py bash
次に、入力した文字は、入力するとすぐに端末に表示されます。これは、対話型アプリケーションにとって非常に重要です。実行すると次のようになります。
bash | tee logfile.txt
また、出力をすぐにouptutファイルに表示したい場合は、次を追加することもできます。
logfile.flush()
しかし、tee
はこれを行いません。パフォーマンスが低下するのではないかと心配しています。これを簡単にテストできます:
tail -f logfile.txt
関連質問: サブプロセスコマンドからのライブ出力
Ubuntu 18.04でテスト済み、Python 3.6.7。