1

Python からスクリプトを呼び出して、その出力を収集する必要があります。そう、

p = subprocess.Popen ("script", shell = False, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
out_lines = p.communicate ("\n".join (in_lines)) [0]

out_line...最悪の事態が発生した場合(サブプロセスまたはメインプロセスのいずれか)に備えて、すべてをログに記録したい場合を除きます。

私は持っている

  1. 制御不能script
  2. communicate()Pythonのソース コードをコピーしてパッチを適用したくない
  3. スクリプトが各入力行に対して出力行を返すという保証はありません。
  4. プラットフォーム依存の tee ユーティリティの呼び出しは避けたほうがよいでしょう。

これらの 4 つの実行可能だが不便な解決策を除いて、私が見落としているものは他にありますか? stdout = PIPEおそらくロギングラッパーに置き換えるようなものですか?

ありがとうございました。私は一週間ずっとここにいます。

4

3 に答える 3

2

基本的に、重複する 2 つの制御スレッドがあります。

  1. 入力をサブプロセスに送信します。
  2. サブプロセスからデータが利用可能になったときに読み取ります。

プラットフォームに依存しない方法でこれを行うと、スレッド (または選択ループ) を使用する以外に多くのオプションが得られません。

問題のコードは標準出力にのみ関心があるように見えるため、標準出力を読み取って内容をファイルに書き込むスレッドを呼び出すだけで済みます。

次に例を示します。

import subprocess
import os
import threading


class LogThread(threading.Thread):
    """Thread which will read from `pipefd` and write all contents to
    `fileobj` until `pipefd` is closed.  Used as a context manager, this thread
    will be automatically started, and joined on exit, usually when the
    child process exits.
    """
    def __init__(self, pipefd, fileobj):
        self.pipefd = pipefd
        self.fileobj = fileobj
        super(LogThread, self).__init__()
        self.setDaemon(1)
        self.start()

    def run(self):
        while True:
            line = self.pipefd.readline()
            if not line:
                break
            self.fileobj.write(line)
            self.fileobj.flush()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self.join()


# Here's how to use the LogThread.    
p = subprocess.Popen ("script", shell = False, stdin = subprocess.PIPE, stdout = subprocess.PIPE)
with open('logfile.txt', 'wt') as logfile:
    with LogThread(p.stdout, logfile):
        p.stdin.write("\n".join(in_lines))
        p.stdin.close()

これはおそらく の小さな部分を複製Popen.communicate()しますが、多くのコードではなく、プラットフォームに依存しません。

バッファリングに関する注意: stdout が非 tty デバイス (パイプなど) にバッファリングされるのは正常です。通常、stderr はバッファリングされません。通常、実行中のアプリケーションがその出力をバッファリングするかどうかを制御することはできません。せいぜい、バッファリングを使用するかどうかを決定する方法を推測することができます。ほとんどのアプリケーションは、バッファリングするisatty()必要があるかどうかを決定するために呼び出します。そのため、ログ ファイルにバッファリング 0 を設定することは、バッファリングを回避するための正しい解決策ではない可能性があります。バッファリングが 0 の場合、出力の各文字は単一のwrite()呼び出しとして書き込まれ、非常に非効率的です。上記のソリューションは、行バッファリングを実行するように変更されました。

次のリンクが役に立つかもしれません: https://unix.stackexchange.com/questions/25372/turn-off-buffering-in-pipe

于 2013-10-17T12:15:26.863 に答える
1

のアクションはsubprocess.communicate、プラットフォームの検出に依存しています。Windows では、作業はスレッドを使用して行われ、ロギングの目的にはファイル ラッパーを使用するだけで十分です。

ただし、Unix ではsubprocessselectファイル記述子 ( ) の取得に依存する を使用するfile.fileno()ため、この手法は機能しません。別のパイプを作成して出力を python で複製することもできますが、もう少し複雑です。また、とにかくプラットフォームに依存するコードを書いているので、Unix では通常tee、その正確な目的のためにコマンドを使用できます。

これを知っているので、要件を満たすプラットフォーム依存の例を次に示します。

import subprocess
import sys

class FileWrapperWithLog(object):
    def __init__(self, file_object, filename):
        self.f= file_object
        self.log= open(filename, 'wb')
    def read(self):
        data= self.f.read()
        self.log.write(data)
        return data
    def close(self):
        return self.f.close()

FILENAME="my_file.log"
if sys.platform == "win32":
    p= subprocess.Popen('dir', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    p.stdout= FileWrapperWithLog( p.stdout, FILENAME )
else:
    p= subprocess.Popen('ls | tee '+FILENAME, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
p.communicate()

もう 1 つのオプションはモンキー パッチsubprocessですが、これはエラーが発生しやすいプロセスです。これは、通信が複雑な方法であり、前述のプラットフォームに依存する動作があるためです。

于 2013-10-17T10:58:25.290 に答える
1

次の簡単なスクリプトは、使用できるアプローチ (クロスプラットフォーム) を示しています。

from subprocess import Popen, PIPE
import sys
import threading

def handle_line(line):
    print(line) # or log it, or whatever

def reader(stream):
    while True:
        s = stream.readline()
        if not s:
            break
        handle_line(s)
    stream.close()

p = Popen(sys.argv[1].split(), stdout=PIPE, stderr=PIPE, stdin=PIPE)
# Get threads  ready to read the subprocess output
out_reader = threading.Thread(target=reader, args=(p.stdout,))
err_reader = threading.Thread(target=reader, args=(p.stderr,))
out_reader.start()
err_reader.start()
# Provide the subprocess input
p.stdin.write("Hello, world!")
p.stdin.close()
# Wait for the child process to complete
p.wait()
# And for all its output to be consumed
out_reader.join()
err_reader.join()
print('Done.')

(または、Windows では Gnu-Win32 )stdinなどの、その をエコーするプログラムで実行すると、次のようになります。catcat.exe

Hello, world!
Done.

出力として。これは、より大きな出力で動作するはずです - 私はこのテクニックを で使用しpython-gnupgます.stderrcommunicate

更新:「OOP の優れた機能」を構成する方法はたくさんあります。特に、Austin Phillips のバージョンが役立つとは思いません。ただし、個々のニーズに応じて、最も簡単な方法で実行する必要があり、その上に構築できる手順を示しました。

于 2013-10-17T22:47:20.130 に答える