操作のニース進行状況インジケーターを表示するコマンドラインプログラム(svnadmin verify)のラッパースクリプトを記述しようとしています。これには、ラップされたプログラムからの出力の各行が出力されるとすぐに見ることができる必要があります。
subprocess.Popen
を使用してプログラムを実行し、stdout=PIPE
を使用してから、各行を読み込んで、それに応じて処理するだけだと考えました。ただし、次のコードを実行すると、出力がどこかにバッファリングされているように見え、1行目から332行目、次に333から439行目(出力の最後の行)の2つのチャンクに表示されます
from subprocess import Popen, PIPE, STDOUT
p = Popen('svnadmin verify /var/svn/repos/config', stdout = PIPE,
stderr = STDOUT, Shell = True)
for line in p.stdout:
print line.replace('\n', '')
サブプロセスのドキュメントを少し見て、bufsize
パラメーターがPopen
になっていることを発見したので、bufsizeを1(各行のバッファー)と0(バッファーなし)に設定しようとしましたが、値はありません線の配達方法を変えたようです。
この時点でストローを把握し始めていたので、次の出力ループを作成しました。
while True:
try:
print p.stdout.next().replace('\n', '')
except StopIteration:
break
しかし、同じ結果を得ました。
サブプロセスを使用して実行されたプログラムの「リアルタイム」プログラム出力を取得することは可能ですか? Pythonにはexec*
ではなく、前方互換性がある)他のオプションがありますか?
私はこれを試しましたが、何らかの理由でコードが
for line in p.stdout:
...
積極的にバッファ、バリアント
while True:
line = p.stdout.readline()
if not line: break
...
ではない。明らかにこれは既知のバグです: http://bugs.python.org/issue3907 (2018年8月29日現在、この問題は「クローズ」です)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=1)
for line in iter(p.stdout.readline, b''):
print line,
p.stdout.close()
p.wait()
これを試すことができます:
import subprocess
import sys
process = subprocess.Popen(
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
while True:
out = process.stdout.read(1)
if out == '' and process.poll() != None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
Readの代わりにreadlineを使用すると、入力メッセージが出力されない場合があります。インライン入力が必要なコマンドで試してみてください。
サブプロセスの出力を直接ストリームに送信できます。簡単な例:
subprocess.run(['ls'], stderr=sys.stderr, stdout=sys.stdout)
リアルタイム出力の問題が解決されました:cプログラムからリアルタイム出力をキャプチャしているときに、Pythonで同様の問題が発生しました。 「fflush(stdout);」を追加しました私のCコードで。それは私のために働いた。ここにコードの抜粋があります
<< Cプログラム>>
#include <stdio.h>
void main()
{
int count = 1;
while (1)
{
printf(" Count %d\n", count++);
fflush(stdout);
sleep(1);
}
}
<< Python Program >>
#!/usr/bin/python
import os, sys
import subprocess
procExe = subprocess.Popen(".//count", Shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
while procExe.poll() is None:
line = procExe.stdout.readline()
print("Print:" + line)
<<出力>>印刷:カウント1印刷:カウント2印刷:カウント3
それが役に立てば幸い。
〜サイラム
私はしばらく前に同じ問題に遭遇しました。私の解決策は、サブプロセスの実行が完了していなくてもすぐに戻るread
メソッドの反復処理を終了することでした。
PythonのasyncioでのサブプロセスstdinおよびstdoutのストリーミングKevin McCarthy によるブログ投稿では、asyncioでそれを行う方法を示しています。
import asyncio
from asyncio.subprocess import PIPE
from asyncio import create_subprocess_exec
async def _read_stream(stream, callback):
while True:
line = await stream.readline()
if line:
callback(line)
else:
break
async def run(command):
process = await create_subprocess_exec(
*command, stdout=PIPE, stderr=PIPE
)
await asyncio.wait(
[
_read_stream(
process.stdout,
lambda x: print(
"STDOUT: {}".format(x.decode("UTF8"))
),
),
_read_stream(
process.stderr,
lambda x: print(
"STDERR: {}".format(x.decode("UTF8"))
),
),
]
)
await process.wait()
async def main():
await run("docker build -t my-docker-image:latest .")
if __== "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
サブプロセスの出力の各バイトに対してイテレータを使用できます。これにより、サブプロセスからのインライン更新(「\ r」で終わる行が前の出力行を上書きする)が可能になります。
from subprocess import PIPE, Popen
command = ["my_command", "-my_arg"]
# Open pipe to subprocess
subprocess = Popen(command, stdout=PIPE, stderr=PIPE)
# read each byte of subprocess
while subprocess.poll() is None:
for c in iter(lambda: subprocess.stdout.read(1) if subprocess.poll() is None else {}, b''):
c = c.decode('ascii')
sys.stdout.write(c)
sys.stdout.flush()
if subprocess.returncode != 0:
raise Exception("The subprocess did not terminate correctly.")
このソリューションを使用して、サブプロセスでリアルタイムの出力を取得しました。このループは、プロセスが完了するとすぐに停止し、breakステートメントまたは無限ループの可能性がなくなります。
sub_process = subprocess.Popen(my_command, close_fds=True, Shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while sub_process.poll() is None:
out = sub_process.stdout.read(1)
sys.stdout.write(out)
sys.stdout.flush()
この「プラグアンドプレイ」機能を見つけました here 。魅力のように働いた!
import subprocess
def myrun(cmd):
"""from http://blog.kagesenshi.org/2008/02/teeing-python-subprocesspopen-output.html
"""
p = subprocess.Popen(cmd, Shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout = []
while True:
line = p.stdout.readline()
stdout.append(line)
print line,
if line == '' and p.poll() != None:
break
return ''.join(stdout)
非ブロッキングリードラインでpexpect [ http://www.noah.org/wiki/Pexpect ]を使用すると、この問題は解決します。これは、パイプがバッファリングされているため、アプリの出力がパイプによってバッファリングされているため、バッファがいっぱいになるかプロセスが終了するまでその出力に到達できません。
これは私がいつもこれに使用する基本的なスケルトンです。これにより、タイムアウトの実装が容易になり、避けられないハングプロセスに対処できます。
import subprocess
import threading
import Queue
def t_read_stdout(process, queue):
"""Read from stdout"""
for output in iter(process.stdout.readline, b''):
queue.put(output)
return
process = subprocess.Popen(['dir'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
bufsize=1,
cwd='C:\\',
Shell=True)
queue = Queue.Queue()
t_stdout = threading.Thread(target=t_read_stdout, args=(process, queue))
t_stdout.daemon = True
t_stdout.start()
while process.poll() is None or not queue.empty():
try:
output = queue.get(timeout=.5)
except Queue.Empty:
continue
if not output:
continue
print(output),
t_stdout.join()
(このソリューションはPython 2.7.15)でテスト済みです)
各行の読み取り/書き込み後にsys.stdout.flush()を実行するだけです。
while proc.poll() is None:
line = proc.stdout.readline()
sys.stdout.write(line)
# or print(line.strip()), you still need to force the flush.
sys.stdout.flush()
完全なソリューション:
import contextlib
import subprocess
# Unix, Windows and old Macintosh end-of-line
newlines = ['\n', '\r\n', '\r']
def unbuffered(proc, stream='stdout'):
stream = getattr(proc, stream)
with contextlib.closing(stream):
while True:
out = []
last = stream.read(1)
# Don't loop forever
if last == '' and proc.poll() is not None:
break
while last not in newlines:
# Don't loop forever
if last == '' and proc.poll() is not None:
break
out.append(last)
last = stream.read(1)
out = ''.join(out)
yield out
def example():
cmd = ['ls', '-l', '/']
proc = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
# Make all end-of-lines '\n'
universal_newlines=True,
)
for line in unbuffered(proc):
print line
example()