_subprocess.Popen
_呼び出しからstdout
を取得しようとしていますが、次のようにすることで簡単にこれを実現できます。
_cmd = subprocess.Popen('ls -l', Shell=True, stdout=PIPE)
for line in cmd.stdout.readlines():
print line
_
「リアルタイム」でstdout
を取得したいと思います。上記のメソッドでは、PIPEはすべてのstdout
を取得するのを待ってから、戻ります。
したがって、ロギングの目的では、これは私の要件を満たしていません(たとえば、発生中に何が起こっているかを「確認」します)。
実行中に行ごとにstdout
を取得する方法はありますか?または、これはsubprocess
の制限ですか(PIPE
が閉じるまで待機する必要があります)。
[〜#〜] edit [〜#〜]readlines()
をreadline()
に切り替えると、stdout
の最後の行しか取得されません(理想的ではありません):
_In [75]: cmd = Popen('ls -l', Shell=True, stdout=PIPE)
In [76]: for i in cmd.stdout.readline(): print i
....:
t
o
t
a
l
1
0
4
_
あなたの通訳はバッファリングしています。 printステートメントの後にsys.stdout.flush()への呼び出しを追加します。
実際、実際の解決策は、サブプロセスのstdoutをプロセスのstdoutに直接リダイレクトすることです。
実際、あなたのソリューションでは、たとえば、stdoutのみを印刷でき、stderrは同時に印刷できません。
_import sys
from subprocess import Popen
Popen("./slow_cmd_output.sh", stdout=sys.stdout, stderr=sys.stderr).communicate()
_
communicate()
は、サブプロセスが終了するまで呼び出しをブロックするためのものです。そうしないと、次の行に直接移動し、サブプロセスの前にプログラムが終了する可能性があります(ただし、stdoutへのリダイレクトは引き続き機能しますが、 pythonスクリプトが閉じた後でも、テストしました)。
そのようにして、たとえば、stdoutとstderrの両方を絶対リアルタイムでリダイレクトします。
たとえば、私の場合、このスクリプトでテストしました_slow_cmd_output.sh
_:
_#!/bin/bash
for i in 1 2 3 4 5 6; do sleep 5 && echo "${i}th output" && echo "err output num ${i}" >&2; done
_
出力を合体させているreadlines()を削除します。また、ほとんどのコマンドはパイプへの出力を内部的にバッファリングするため、ラインバッファリングを強制する必要があります。詳細については、以下を参照してください。 http://www.pixelbeat.org/programming/stdio_buffering/
これは私が何日も答えを探していた質問なので、フォローしている人のためにここに残したいと思いました。 subprocess
が他のプロセスのバッファリング戦略と戦うことができないのは事実ですが、別のPythonスクリプトをsubprocess.Popen
で呼び出す場合は、バッファリングされていないPythonを起動します。
command = ["python", "-u", "python_file.py"]
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, ''):
line = line.replace('\r', '').replace('\n', '')
print line
sys.stdout.flush()
また、仮引数bufsize=1
とuniversal_newlines=True
が非表示のstdout
の公開に役立った場合もあります。
cmd = subprocess.Popen(["ls", "-l"], stdout=subprocess.PIPE)
for line in cmd.stdout:
print line.rstrip("\n")
すでに述べたように、問題は、プロセスに端末が接続されていない場合のstdioライブラリのprintflikeステートメントのバッファリングにあります。とにかく、Windowsプラットフォームでこれを回避する方法があります。他のプラットフォームでも同様の解決策があるかもしれません。
Windowsでは、プロセスの作成時に新しいコンソールを強制的に作成できます。良い点は、これを非表示のままにして、表示されないようにすることです(これは、サブプロセスモジュール内のShell = Trueによって実行されます)。
cmd = subprocess.Popen('ls -l', Shell=True, stdout=PIPE, creationflags=_winapi.CREATE_NEW_CONSOLE, bufsize=1, universal_newlines=True)
for line in cmd.stdout.readlines():
print line
または
もう少し完全な解決策は、STARTUPINFO paramsを明示的に設定して、Shell = Trueが上記で行った新しい不要なcmd.exeシェルプロセスの起動を防ぐことです。
class PopenBackground(subprocess.Popen):
def __init__(self, *args, **kwargs):
si = kwargs.get('startupinfo', subprocess.STARTUPINFO())
si.dwFlags |= _winapi.STARTF_USESHOWWINDOW
si.wShowWindow = _winapi.SW_HIDE
kwargs['startupinfo'] = si
kwargs['creationflags'] = kwargs.get('creationflags', 0) | _winapi.CREATE_NEW_CONSOLE
kwargs['bufsize'] = 1
kwargs['universal_newlines'] = True
super(PopenBackground, self).__init__(*args, **kwargs)
process = PopenBackground(['ls', '-l'], stdout=subprocess.PIPE)
for line in cmd.stdout.readlines():
print line
readlines
の呼び出しは、プロセスが終了するのを待っています。これをcmd.stdout.readline()
(単数に注意)の周りのループに置き換えれば、すべてうまくいくはずです。