Windowsでsubprocess.Popen()
rsync.exeを実行し、Pythonで標準出力を出力します。
私のコードは動作しますが、ファイル転送が完了するまで進行状況をキャッチしません!各ファイルの進行状況をリアルタイムで印刷したい。
Python 3.1を使用するようになりました。IOの処理が改善されるはずだと聞いたからです。
import subprocess, time, os, sys
cmd = "rsync.exe -vaz -P source/ dest/"
p, line = True, 'start'
p = subprocess.Popen(cmd,
Shell=True,
bufsize=64,
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
for line in p.stdout:
print(">>> " + str(line.rstrip()))
p.stdout.flush()
subprocess
の経験則。
Shell=True
。プログラムを呼び出すために、余分なシェルプロセスを不必要に呼び出します。sys.argv
in pythonはリストであり、Cのargv
も同様です。したがって、listをPopen
に渡して呼び出します。文字列ではなく、サブプロセス。stderr
をPIPE
にリダイレクトしないでください。stdin
をリダイレクトしないでください。例:
import subprocess, time, os, sys
cmd = ["rsync.exe", "-vaz", "-P", "source/" ,"dest/"]
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
for line in iter(p.stdout.readline, b''):
print(">>> " + line.rstrip())
とは言っても、rsyncが端末ではなくパイプに接続されていることを検出すると、rsyncが出力をバッファリングする可能性があります。これはデフォルトの動作です-パイプに接続する場合、プログラムはリアルタイムの結果のためにstdoutを明示的にフラッシュする必要があります。そうしないと、標準Cライブラリがバッファリングします。
それをテストするには、代わりにこれを実行してみてください:
cmd = [sys.executable, 'test_out.py']
test_out.py
ファイルの内容:
import sys
import time
print ("Hello")
sys.stdout.flush()
time.sleep(10)
print ("World")
そのサブプロセスを実行すると、「Hello」が表示され、「World」が表示されるまで10秒待機します。上記のコードでpythonが発生し、rsync
では発生しない場合は、rsync
自体が出力をバッファリングしているため、運が悪いのです。
解決策は、pty
のようなものを使用して、pexpect
に直接接続することです。
私はこれが古いトピックであることを知っていますが、現在解決策があります。オプション--outbuf = Lを指定してrsyncを呼び出します。例:
cmd=['rsync', '-arzv','--backup','--outbuf=L','source/','dest']
p = subprocess.Popen(cmd,
stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, b''):
print '>>> {}'.format(line.rstrip())
Linuxでは、バッファリングを取り除くという同じ問題がありました。私は最終的に「stdbuf -o0」(または、expectからアンバッファー)を使用して、PIPEバッファリングを取り除きました。
proc = Popen(['stdbuf', '-o0'] + cmd, stdout=PIPE, stderr=PIPE)
stdout = proc.stdout
その後、stdoutでselect.selectを使用できます。
_for line in p.stdout:
...
_
次の改行まで常にブロックします。
「リアルタイム」動作の場合、次のような操作を行う必要があります。
_while True:
inchar = p.stdout.read(1)
if inchar: #neither empty string nor None
print(str(inchar), end='') #or end=None to flush immediately
else:
print('') #flush for implicit line-buffering
break
_
子プロセスが標準出力を閉じるか終了すると、whileループが残ります。 read()/read(-1)
は、子プロセスが標準出力を閉じるか終了するまでブロックします。
あなたの問題は:
for line in p.stdout:
print(">>> " + str(line.rstrip()))
p.stdout.flush()
イテレータ自体には追加のバッファリングがあります。
このようにしてみてください:
while True:
line = p.stdout.readline()
if not line:
break
print line
Stdoutをバッファなしでパイプに出力することはできません(stdoutに出力するプログラムを書き換えることができない限り)。ここに私の解決策を示します。
Stdoutをsterrにリダイレクトします。これはバッファリングされません。 _'<cmd> 1>&2'
_はそれを行う必要があります。次のようにプロセスを開きます:myproc = subprocess.Popen('<cmd> 1>&2', stderr=subprocess.PIPE)
stdoutまたはstderrと区別することはできませんが、すべての出力をすぐに取得できます。
これがこの問題に取り組む誰にも役立つことを願っています。
ユースケースによっては、サブプロセス自体のバッファリングを無効にすることもできます。
サブプロセスがPythonプロセスである場合、呼び出しの前にこれを行うことができます。
os.environ["PYTHONUNBUFFERED"] = "1"
または、これをenv
引数でPopen
に渡します。
それ以外の場合、Linux/Unixを使用している場合は、stdbuf
ツールを使用できます。例えば。好む:
cmd = ["stdbuf", "-oL"] + cmd
here about stdbuf
またはその他のオプションも参照してください。
出力のキャッシュを回避するには、pexpectを試してみてください。
child = pexpect.spawn(launchcmd,args,timeout=None)
while True:
try:
child.expect('\n')
print(child.before)
except pexpect.EOF:
break
[〜#〜] ps [〜#〜]:私はこの質問がかなり古いことを知っています。
[〜#〜] pps [〜#〜]:別の質問からこの回答を得た
p = subprocess.Popen(command,
bufsize=0,
universal_newlines=True)
私はPythonでrsync用のGUIを書いていますが、同じ問題があります。この問題は、pyDocでこれを見つけるまで数日間悩みました。
Universal_newlinesがTrueの場合、ファイルオブジェクトstdoutおよびstderrは、ユニバーサル改行モードでテキストファイルとして開かれます。行は、 '\ n'(Unixの行末規則)、 '\ r'(古いMacintosh規則)、または '\ r\n'(Windows規則)のいずれかで終了できます。これらの外部表現はすべて、Pythonプログラムによって「\ n」として認識されます。
翻訳が進行中の場合、rsyncは「\ r」を出力するようです。
Rsyncプロセスの標準出力をバッファなしに変更します。
p = subprocess.Popen(cmd,
Shell=True,
bufsize=0, # 0=unbuffered, 1=line-buffered, else buffer-size
stdin=subprocess.PIPE,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)
中間ファイルとして一時ファイルを使用することに言及していないことに気付きました。以下は、一時ファイルに出力することでバッファリングの問題を回避し、ptyに接続せずにrsyncからのデータを解析できるようにします。 Linuxボックスで以下をテストしましたが、rsyncの出力はプラットフォーム間で異なる傾向があるため、出力を解析する正規表現は異なる場合があります。
import subprocess, time, tempfile, re
pipe_output, file_name = tempfile.TemporaryFile()
cmd = ["rsync", "-vaz", "-P", "/src/" ,"/dest"]
p = subprocess.Popen(cmd, stdout=pipe_output,
stderr=subprocess.STDOUT)
while p.poll() is None:
# p.poll() returns None while the program is still running
# sleep for 1 second
time.sleep(1)
last_line = open(file_name).readlines()
# it's possible that it hasn't output yet, so continue
if len(last_line) == 0: continue
last_line = last_line[-1]
# Matching to "[bytes downloaded] number% [speed] number:number:number"
match_it = re.match(".* ([0-9]*)%.* ([0-9]*:[0-9]*:[0-9]*).*", last_line)
if not match_it: continue
# in this case, the percentage is stored in match_it.group(1),
# time in match_it.group(2). We could do something with it here...