web-dev-qa-db-fra.com

Exécuter le sous-processus et imprimer la sortie dans la journalisation

Je cherche le moyen d'appeler des scripts Shell depuis python et d'écrire leur stdout et stderr dans un fichier en utilisant la journalisation. Voici mon code:

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

Et je suis sûr qu'il existe un moyen de le faire sans créer de fichier temporaire pour stocker la sortie du processus. Des idées?

27
Kostya Bazhanov

Je suis sûr qu'il existe un moyen de le faire sans créer de fichier temporaire pour stocker la sortie du processus

Il vous suffit de vérifier la documentation de Popen , en particulier sur stdout et stderr:

stdin, stdout et stderr spécifient respectivement les descripteurs d'entrée, de sortie standard et de fichier d'erreur standard du programme exécuté. Les valeurs valides sont PIPE, un descripteur de fichier existant (un entier positif), un objet fichier existant et None. PIPE indique qu'un nouveau canal vers l'enfant doit être créé. Avec les paramètres par défaut de None, aucune redirection ne se produira; les descripteurs de fichiers de l'enfant seront hérités du parent. De plus, stderr peut être STDOUT, ce qui indique que les données stderr du processus enfant doivent être capturées dans le même descripteur de fichier que pour stdout.

Ainsi, vous pouvez voir que vous pouvez utiliser un objet fichier ou la valeur PIPE. Cela vous permet d'utiliser la méthode communicate() pour récupérer la sortie:

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

Je réécrirais votre code comme:

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

Vous pouvez essayer de passer le canal directement sans mettre en mémoire tampon la sortie entière du sous-processus en mémoire:

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() pourrait ressembler à:

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