python - 起動 - 標準 出力 リアルタイム




stdoutプロセスをリアルタイムで読み込む (4)

p.stdoutの行が内部的にどのように評価されたか? それはstdout eofか何かに達するまで無限ループの何らかの種類ですか?

p.stdoutはバッファ(ブロッキング)です。 空のバッファから読み取っているときは、何かがそのバッファに書き込まれるまでブロックされます。 何かが内部に入ると、データを取得して内側の部分を実行します。

tail -fがLinux上でどのように動作するか考えてみましょう。何かがファイルに書き込まれるまで待っています。そうすると、新しいデータが画面に表示されます。 データがないとどうなりますか? それは待っている。 したがって、プログラムがこの行に到達すると、データを待って処理します。

コードは機能しますが、モデルとして実行されない場合は、何らかの形でこれに関連している必要があります。 おそらくhttp.serverモジュールが出力をバッファします。 プロセスをunbufferedとして実行するには、 -uパラメータをPythonに追加してみてください:

-u:バッファされていないバイナリのstdoutとstderr。 PYTHONUNBUFFERED = x「-u」に関する内部バッファリングの詳細については、manページを参照してください。

また、 for line in iter(lambda: p.stdout.read(1), ''):ループを変更するとよいでしょう。これは、処理の前に一度に1バイトを読み込むためです。

更新 :完全なループコードは

for line in iter(lambda: p.stdout.read(1), ''):
    sys.stdout.write(line)
    sys.stdout.flush()

また、コマンドを文字列として渡します。 それをリストとして渡して、各要素をそれ自身のスロットに入れてみてください:

cmd = ['python', '-m', 'http.server', ..]

このスニペットを考えてみましょう:

from subprocess import Popen, PIPE, CalledProcessError


def execute(cmd):
    with Popen(cmd, shell=True, stdout=PIPE, bufsize=1, universal_newlines=True) as p:
        for line in p.stdout:
            print(line, end='')

    if p.returncode != 0:
        raise CalledProcessError(p.returncode, p.args)

base_cmd = [
    "cmd", "/c", "d:\\virtual_envs\\py362_32\\Scripts\\activate",
    "&&"
]
cmd1 = " ".join(base_cmd + ['python -c "import sys; print(sys.version)"'])
cmd2 = " ".join(base_cmd + ["python -m http.server"])

私がexecute(cmd1)を実行execute(cmd1) 、出力は問題なく印刷されます。

しかし、 execute(cmd2)を実行execute(cmd2) 、何も印刷されないのですが、どうして私はそれを修正して、http.serverの出力をリアルタイムで見ることができますか?

また、 for line in p.stdout内部的にどのようfor line in p.stdout評価されていますか? それはstdout eofか何かに達するまで無限ループの何らかの種類ですか?

このトピックは既にここで数回言及されていますが、Windowsソリューションはまだ見つかりませんでした。 上のスニペットは実際このanswerコードであり、virtualenv(http:// win7のpython3.6.2-32bits)からhttp.serverを実行しようとしています。


OSレベルでバッファなしの動作を実装できます。

Linuxでは、既存のコマンドラインをstdbufラップすることができます:

stdbuf -i0 -o0 -e0 YOURCOMMAND

Windowsでは、既存のコマンドラインをwinptyラップすることができます:

winpty.exe -Xallow-non-tty -Xplain YOURCOMMAND

私はこれについてOSに中立なツールを知らない。


実行中のサブプロセスから連続して読み込みたい場合は、 そのプロセスの出力をバッファなしにする必要があります。 あなたのサブプロセスはPythonプログラムです。これは、 -uをインタプリタに渡すことで実行できます:

python -u -m http.server

これはWindowsのボックスに表示されます。


私は主な問題は、 http.server何とかstderrに出力を記録しているということだと思います。ここでは、 asyncio例がasynciostdoutまたはstderrからデータを読み込みます。

私の最初の試みは、Python 3.4以降に存在する素晴らしいAPI asyncioを使うことでした。 後で私は簡単な解決法を見つけました。あなたが選ぶことができるので、emの両方がうまくいくはずです。

ソリューションとしてのasyncio

バックグラウンドでは、asyncioはIOCPを使用しています - ウィンドウAPIを非同期のものにします。

# inspired by https://pymotw.com/3/asyncio/subprocesses.html

import asyncio
import sys
import time

if sys.platform == 'win32':
    loop = asyncio.ProactorEventLoop()
    asyncio.set_event_loop(loop)

async def run_webserver():
    buffer = bytearray()

    # start the webserver without buffering (-u) and stderr and stdin as the arguments
    print('launching process')
    proc = await asyncio.create_subprocess_exec(
        sys.executable, '-u', '-mhttp.server',
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.PIPE
    )

    print('process started {}'.format(proc.pid))
    while 1:
        # wait either for stderr or stdout and loop over the results
        for line in asyncio.as_completed([proc.stderr.readline(), proc.stdout.readline()]):
            print('read {!r}'.format(await line))

event_loop = asyncio.get_event_loop()
try:
    event_loop.run_until_complete(run_df())
finally:
    event_loop.close()

からstdoutをリダイレクトする

あなたの例に基づいて、これは本当に簡単な解決策です。 stderrをstdoutにリダイレクトし、stdoutだけを読み込みます。

from subprocess import Popen, PIPE, CalledProcessError, run, STDOUT import os

def execute(cmd):
    with Popen(cmd, stdout=PIPE, stderr=STDOUT, bufsize=1) as p:
        while 1:
            print('waiting for a line')
            print(p.stdout.readline())

cmd2 = ["python", "-u", "-m", "http.server"]

execute(cmd2)






popen