Подтвердить что ты не робот

Запуск подпроцесса и вывода на печать для ведения журнала

Я ищу способ вызова shell-скриптов из 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

И я уверен, что есть способ сделать это, не создавая временный файл для хранения выходных данных процесса. Любые идеи?

4b9b3361

Ответ 1

Я уверен, что есть способ сделать это, не создавая временных файл для хранения выходных данных процесса

Вам просто нужно проверить документацию Popen, в частности о stdout и stderr:

stdin, stdout и stderr указать стандартные исполняемые программы вход, стандартный вывод и стандартные файлы файлов ошибок соответственно. Допустимыми значениями являются PIPE, существующий файловый дескриптор (положительный целое), существующий файловый объект и None. PIPE указывает, что a должен быть создан новый канал для ребенка. С настройками по умолчанию None, перенаправление не произойдет; файлы файлов childs будут унаследованный от родителя. Кроме того, 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

Ответ 2

Вы можете попытаться передать канал напрямую, не буферизируя весь вывод подпроцесса в памяти:

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)