web-dev-qa-db-ja.com

サブプロセスを実行し、出力をログに出力します

pythonからシェルスクリプトを呼び出し、ロギングを使用してstdoutとstderrをファイルに書き込む方法を探しています。以下にコードを示します。

import logging
import tempfile
import shlex
import os

def run_Shell_command(command_line):
    command_line_args = shlex.split(command_line)

    logging.info('Subprocess: \"' + command_line + '\"')

    process_succeeded = True
    try:
        process_output_filename = tempfile.mktemp(suffix = 'subprocess_tmp_file_')
        process_output = open(process_output_filename, 'w')

        command_line_process = subprocess.Popen(command_line_args,\
                                                stdout = process_output,\
                                                stderr = process_output)
        command_line_process.wait()
        process_output.close()

        process_output = open(process_output_filename, 'r')
        log_subprocess_output(process_output)
        process_output.close()

        os.remove(process_output_filename)
    except:
        exception = sys.exc_info()[1]
        logging.info('Exception occured: ' + str(exception))
        process_succeeded = False

    if process_succeeded:
        logging.info('Subprocess finished')
    else:
        logging.info('Subprocess failed')

    return process_succeeded

そして、プロセス出力を保存する一時ファイルを作成せずにそれを行う方法があると確信しています。何か案は?

27
Kostya Bazhanov

プロセス出力を保存する一時ファイルを作成せずにそれを行う方法があると確信しています

Popen のドキュメント、特にstdoutstderrについて確認するだけです。

stdinstdout、およびstderrは、実行されたプログラムの標準入力、標準出力、および標準エラーファイルハンドルをそれぞれ指定します。有効な値は、PIPE、既存のファイル記述子(正の整数)、既存のファイルオブジェクト、およびNoneです。 PIPEは、子への新しいパイプを作成する必要があることを示します。 Noneのデフォルト設定では、リダイレクトは発生しません。子のファイルハンドルは親から継承されます。さらに、stderrにはSTDOUTを指定できます。これは、子プロセスからのstderrデータを、stdoutと同じファイルハンドルにキャプチャする必要があることを示します。

したがって、ファイルオブジェクトまたはPIPE値を使用できることがわかります。これにより、 communicate() メソッドを使用して出力を取得できます。

from StringIO import StringIO
process = subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output, error = process.communicate()
log_subprocess_output(StringIO(output))

コードを次のように書き直します。

import shlex
import logging
import subprocess
from StringIO import StringIO

def run_Shell_command(command_line):
    command_line_args = shlex.split(command_line)

    logging.info('Subprocess: "' + command_line + '"')

    try:
        command_line_process = subprocess.Popen(
            command_line_args,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
        )

        process_output, _ =  command_line_process.communicate()

        # process_output is now a string, not a file,
        # you may want to do:
        # process_output = StringIO(process_output)
        log_subprocess_output(process_output)
    except (OSError, CalledProcessError) as exception:
        logging.info('Exception occured: ' + str(exception))
        logging.info('Subprocess failed')
        return False
    else:
        # no exception was raised
        logging.info('Subprocess finished')

    return True
20
Bakuriu

サブプロセスの出力全体をメモリにバッファリングせずに、パイプを直接渡そうとすることができます。

_from subprocess import Popen, PIPE, STDOUT

process = Popen(command_line_args, stdout=PIPE, stderr=STDOUT)
with process.stdout:
    log_subprocess_output(process.stdout)
exitcode = process.wait() # 0 means success
_

log_subprocess_output()は次のようになります。

_def log_subprocess_output(pipe):
    for line in iter(pipe.readline, b''): # b'\n'-separated lines
        logging.info('got line from subprocess: %r', line)
_
25
jfs