web-dev-qa-db-ja.com

Pythonでサブプロセスを使用するときにティーの動作を複製する方法は?

コンソールから非表示にせずにコマンドの出力をファイルに保存できるPythonソリューションを探しています。

参考までに、私は tee (Unixコマンドラインユーティリティとして)について質問しており、Python intertoolsモジュールからの同じ名前の関数については質問していません。

細部

  • Pythonソリューション(teeを呼び出さない、Windowsでは使用できません)
  • 呼び出されたプロセスのためにstdinに入力を提供する必要はありません
  • 呼び出されたプログラムを制御できません。私が知っているのは、それが標準出力と標準エラー出力に何かを出力し、終了コードで戻るということだけです。
  • 外部プログラム(サブプロセス)を呼び出すときに作業するには
  • stderrstdoutの両方で機能する
  • Stdoutとstderrを区別できるため、1つだけをコンソールに表示したり、別の色を使用してstderrを出力したりすることができます。つまり、stderr = subprocess.STDOUT 動作しないでしょう。
  • ライブ出力(プログレッシブ)-プロセスは長時間実行される可能性があり、プロセスが完了するまで待つことができません。
  • Python 3互換コード(重要)

参考文献

ここに私がこれまでに見つけたいくつかの不完全な解決策があります:

図http://blog.i18n.ro/wp-content/uploads/2010/06/Drawing_tee_py.png

現在のコード(2回目の試行)

#!/usr/bin/python
from __future__ import print_function

import sys, os, time, subprocess, io, threading
cmd = "python -E test_output.py"

from threading import Thread
class StreamThread ( Thread ):
    def __init__(self, buffer):
        Thread.__init__(self)
        self.buffer = buffer
    def run ( self ):
        while 1:
            line = self.buffer.readline()
            print(line,end="")
            sys.stdout.flush()
            if line == '':
                break

proc = subprocess.Popen(cmd, Shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutThread = StreamThread(io.TextIOWrapper(proc.stdout))
stderrThread = StreamThread(io.TextIOWrapper(proc.stderr))
stdoutThread.start()
stderrThread.start()
proc.communicate()
stdoutThread.join()
stderrThread.join()

print("--done--")

#### test_output.py ####

#!/usr/bin/python
from __future__ import print_function
import sys, os, time

for i in range(0, 10):
    if i%2:
        print("stderr %s" % i, file=sys.stderr)
    else:
        print("stdout %s" % i, file=sys.stdout)
    time.sleep(0.1)
stderr 1
stdout 0
stderr 3
stdout 2
stderr 5
stdout 4
stderr 7
stdout 6
stderr 9
stdout 8
--done--

予想される出力は、行が順序付けられることでした。注釈:1つのPIPEのみを使用するようにPopenを変更することは許可されていません。現実には、私はstderrとstdoutで異なることをしたいからです。

また、2番目のケースでもリアルタイムのアウトを取得することはできませんでした。実際には、プロセスが終了したときにすべての結果を受け取りました。デフォルトでは、Popenはバッファを使用しません(bufsize = 0)。

65
sorin

これはかなり古い投稿ですが、誰かがこれを行う方法をまだ探している場合に備えて:

proc = subprocess.Popen(["ping", "localhost"], 
                        stdout=subprocess.PIPE, 
                        stderr=subprocess.PIPE)

with open("logfile.txt", "w") as log_file:
  while proc.poll() is None:
     line = proc.stderr.readline()
     if line:
        print "err: " + line.strip()
        log_file.write(line)
     line = proc.stdout.readline()
     if line:
        print "out: " + line.strip()
        log_file.write(line)
16
Ben

最後に、私はtee()コマンドをPython自分で実装する必要がありました。

ここから入手できます http://github.com/pycontribs/tendo/blob/master/tendo/tee.py

現在、次のようなことができます。

 tee("python --v") # works just like os.system()

 tee("python --v", "log.txt") # file names

 tee("python --v", file_handle)

 import logging
 tee("python --v", logging.info) # receive a method

現在の唯一の制限は、stderrstdoutを区別できないことです。つまり、両方をマージします。

13
sorin

これは tee のPythonへの単純な移植です。

_import sys
sinks = sys.argv[1:]
sinks = [open(sink, "w") for sink in sinks]
sinks.append(sys.stderr)
while True:
  input = sys.stdin.read(1024)
  if input:
    for sink in sinks:
      sink.write(input)
  else:
    break
_

私は現在Linuxで実行していますが、これはほとんどのプラットフォームで動作するはずです。


subprocessの部分については、サブプロセスのstdinstdoutおよびstderrstdinstdoutstderrおよびファイルシンクですが、これを実行できることはわかっています。

_import subprocess
callee = subprocess.Popen( ["python", "-i"],
                           stdin = subprocess.PIPE,
                           stdout = subprocess.PIPE,
                           stderr = subprocess.PIPE
                         )
_

これで、通常のファイルと同様に _callee.stdin__callee.stdout_ および _callee.stderr_ にアクセスできるため、上記の「ソリューション」が機能します。 _callee.returncode_ を取得するには、 callee.poll() を追加で呼び出す必要があります。

_callee.stdin_への書き込みには注意してください。そのときにプロセスが終了した場合、エラーが発生する可能性があります(Linuxでは_IOError: [Errno 32] Broken pipe_が表示されます)。

7
badp

python subprocess.PIPEに関連する)に微妙な問題/バグがあります: http://bugs.python.org/issue1652

どうやらこれはpython3 +で修正されましたが、python 2.7以前では修正されていません。そのためには、code.google.com/p/python-subprocess32 /を使用する必要があります。

3
user553965

プロセスとやり取りしたくない場合は、サブプロセスモジュールを使用できます。

例:

tester.py

import os
import sys

for file in os.listdir('.'):
    print file

sys.stderr.write("Oh noes, a shrubbery!")
sys.stderr.flush()
sys.stderr.close()

testing.py

import subprocess

p = subprocess.Popen(['python', 'tester.py'], stdout=subprocess.PIPE,
                     stdin=subprocess.PIPE, stderr=subprocess.PIPE)

stdout, stderr = p.communicate()
print stdout, stderr

あなたの状況では、単純にstdout/stderrを最初にファイルに書き込むことができます。通信を使用してプロセスに引数を送信することもできますが、サブプロセスと継続的に対話する方法を理解することはできませんでした。

3
Wayne Werner

これはそれができる方法です

import sys
from subprocess import Popen, PIPE

with open('log.log', 'w') as log:
    proc = Popen(["ping", "google.com"], stdout=PIPE, encoding='utf-8')
    while proc.poll() is None:
        text = proc.stdout.readline() 
        log.write(text)
        sys.stdout.write(text)
1
Danylo Zhydyk

Pythonでシェルコマンドをラップするものを書きました。

主な利点:

  1. このユーティリティは常にstdout/stderrをキャプチャします
  2. このユーティリティは、プロセスのstdout/stderrをstdout/stderrにエコーするオプションを提供します
  3. Stdout/stderrをエコーするとき、out/errは遅延しません

主な欠点:

  • Bash/unixでのみ機能します

ソース: https://Gist.github.com/AndrewHoos/9f03c74988469b517a7a

0
Andrew Hoos

これを試して :

import sys

class tee-function :

    def __init__(self, _var1, _var2) :

        self.var1 = _var1
        self.var2 = _var2

    def __del__(self) :

        if self.var1 != sys.stdout and self.var1 != sys.stderr :
            self.var1.close()
        if self.var2 != sys.stdout and self.var2 != sys.stderr :
            self.var2.close()

    def write(self, text) :

        self.var1.write(text)
        self.var2.write(text)

    def flush(self) :

        self.var1.flush()
        self.var2.flush()

stderrsav = sys.stderr

out = open(log, "w")

sys.stderr = tee-function(stderrsav, out)

私のソリューションはエレガントではありませんが、機能します。

Powershellを使用して、WinOSで「Tee」にアクセスできます。

import subprocess
import sys

cmd = ['powershell', 'ping', 'google.com', '|', 'tee', '-a', 'log.txt']

if 'darwin' in sys.platform:
    cmd.remove('powershell')

p = subprocess.Popen(cmd)
p.wait()
0
Danylo Zhydyk